refactoring step 2

This commit is contained in:
Ayzen
2025-09-30 14:15:25 +03:00
parent d223865c68
commit c9dc977204
9 changed files with 148 additions and 174 deletions

View File

@ -63,7 +63,7 @@ class VNADashboard {
*/ */
async init() { async init() {
try { try {
console.log('🚀 Initializing VNA Dashboard...'); console.log('Initializing VNA Dashboard...');
// Initialize Lucide icons // Initialize Lucide icons
if (typeof lucide !== 'undefined') { if (typeof lucide !== 'undefined') {
@ -80,7 +80,7 @@ class VNADashboard {
await this.websocket.connect(); await this.websocket.connect();
this.isInitialized = true; this.isInitialized = true;
console.log('VNA Dashboard initialized successfully'); console.log('VNA Dashboard initialized successfully');
// Show welcome notification // Show welcome notification
this.notifications.show({ this.notifications.show({
@ -90,7 +90,7 @@ class VNADashboard {
}); });
} catch (error) { } catch (error) {
console.error('Failed to initialize VNA Dashboard:', error); console.error('Failed to initialize VNA Dashboard:', error);
this.notifications.show({ this.notifications.show({
type: 'error', type: 'error',
title: 'Initialization Failed', title: 'Initialization Failed',
@ -148,7 +148,7 @@ class VNADashboard {
setupUIHandlers() { setupUIHandlers() {
// Navigation // Navigation
this.ui.onViewChange((view) => { this.ui.onViewChange((view) => {
console.log(`📱 Switched to view: ${view}`); console.log(` Switched to view: ${view}`);
if (view === 'settings') { if (view === 'settings') {
this.settings.refresh(); this.settings.refresh();
} }
@ -156,7 +156,7 @@ class VNADashboard {
// Processor toggles (UI-only visibility) // Processor toggles (UI-only visibility)
this.ui.onProcessorToggle((processor, enabled) => { this.ui.onProcessorToggle((processor, enabled) => {
console.log(`🔧 Processor ${processor}: ${enabled ? 'enabled' : 'disabled'}`); console.log(` Processor ${processor}: ${enabled ? 'enabled' : 'disabled'}`);
this.charts.toggleProcessor(processor, enabled); this.charts.toggleProcessor(processor, enabled);
this.savePreferences(); this.savePreferences();
}); });
@ -171,7 +171,7 @@ class VNADashboard {
if (data.type === 'system_status') { if (data.type === 'system_status') {
this.ui.updateSystemStatus(data.data); this.ui.updateSystemStatus(data.data);
} else if (data.type === 'processor_history') { } else if (data.type === 'processor_history') {
console.log('📜 Received processor history:', data); console.log('Received processor history:', data);
} }
// Note: 'error' is handled by WebSocket manager directly // Note: 'error' is handled by WebSocket manager directly
} }
@ -247,7 +247,7 @@ class VNADashboard {
} }
} }
} catch (error) { } catch (error) {
console.warn('⚠️ Error applying preferences:', error); console.warn('Error applying preferences:', error);
} }
} }
@ -263,7 +263,7 @@ class VNADashboard {
}; };
await this.storage.savePreferences(preferences); await this.storage.savePreferences(preferences);
} catch (error) { } catch (error) {
console.warn('⚠️ Failed to save preferences:', error); console.warn('Failed to save preferences:', error);
} }
} }
@ -282,7 +282,7 @@ class VNADashboard {
destroy() { destroy() {
if (!this.isInitialized) return; if (!this.isInitialized) return;
console.log('🧹 Cleaning up VNA Dashboard...'); console.log('Cleaning up VNA Dashboard...');
document.removeEventListener('visibilitychange', this.handleVisibilityChange); document.removeEventListener('visibilitychange', this.handleVisibilityChange);
window.removeEventListener('beforeunload', this.handleBeforeUnload); window.removeEventListener('beforeunload', this.handleBeforeUnload);
@ -295,7 +295,7 @@ class VNADashboard {
this.notifications.destroy(); this.notifications.destroy();
this.isInitialized = false; this.isInitialized = false;
console.log('VNA Dashboard cleanup complete'); console.log('VNA Dashboard cleanup complete');
} }
} }
@ -312,11 +312,11 @@ if (document.readyState === 'loading') {
// Global error handling // Global error handling
window.addEventListener('error', (event) => { window.addEventListener('error', (event) => {
console.error('🚨 Global error:', event.error); console.error('Global error:', event.error);
}); });
window.addEventListener('unhandledrejection', (event) => { window.addEventListener('unhandledrejection', (event) => {
console.error('🚨 Unhandled promise rejection:', event.reason); console.error('Unhandled promise rejection:', event.reason);
}); });
// Development helpers // Development helpers

View File

@ -63,14 +63,14 @@ export class AcquisitionManager {
const result = await response.json(); const result = await response.json();
if (result.success) { if (result.success) {
this.notifications.show({type: 'success', message: result.message}); this.notifications.show({type: 'success', title: 'Acquisition Started', message: result.message});
await this.updateStatus(); await this.updateStatus();
} else { } else {
this.notifications.show({type: 'error', message: result.error || 'Failed to start acquisition'}); this.notifications.show({type: 'error', title: 'Start Failed', message: result.error || 'Failed to start acquisition'});
} }
} catch (error) { } catch (error) {
console.error('Error starting acquisition:', error); console.error('Error starting acquisition:', error);
this.notifications.show({type: 'error', message: 'Failed to start acquisition'}); this.notifications.show({type: 'error', title: 'Start Failed', message: 'Failed to start acquisition'});
} finally { } finally {
setButtonLoading(this.elements.startBtn, false, originalState); setButtonLoading(this.elements.startBtn, false, originalState);
} }
@ -88,14 +88,14 @@ export class AcquisitionManager {
const result = await response.json(); const result = await response.json();
if (result.success) { if (result.success) {
this.notifications.show({type: 'success', message: result.message}); this.notifications.show({type: 'success', title: 'Acquisition Stopped', message: result.message});
await this.updateStatus(); await this.updateStatus();
} else { } else {
this.notifications.show({type: 'error', message: result.error || 'Failed to stop acquisition'}); this.notifications.show({type: 'error', title: 'Stop Failed', message: result.error || 'Failed to stop acquisition'});
} }
} catch (error) { } catch (error) {
console.error('Error stopping acquisition:', error); console.error('Error stopping acquisition:', error);
this.notifications.show({type: 'error', message: 'Failed to stop acquisition'}); this.notifications.show({type: 'error', title: 'Stop Failed', message: 'Failed to stop acquisition'});
} finally { } finally {
setButtonLoading(this.elements.stopBtn, false, originalState); setButtonLoading(this.elements.stopBtn, false, originalState);
} }
@ -113,14 +113,14 @@ export class AcquisitionManager {
const result = await response.json(); const result = await response.json();
if (result.success) { if (result.success) {
this.notifications.show({type: 'success', message: result.message}); this.notifications.show({type: 'success', title: 'Single Sweep', message: result.message});
await this.updateStatus(); await this.updateStatus();
} else { } else {
this.notifications.show({type: 'error', message: result.error || 'Failed to trigger single sweep'}); this.notifications.show({type: 'error', title: 'Single Sweep Failed', message: result.error || 'Failed to trigger single sweep'});
} }
} catch (error) { } catch (error) {
console.error('Error triggering single sweep:', error); console.error('Error triggering single sweep:', error);
this.notifications.show({type: 'error', message: 'Failed to trigger single sweep'}); this.notifications.show({type: 'error', title: 'Single Sweep Failed', message: 'Failed to trigger single sweep'});
} finally { } finally {
setButtonLoading(this.elements.singleSweepBtn, false, originalState); setButtonLoading(this.elements.singleSweepBtn, false, originalState);
} }

View File

@ -68,31 +68,31 @@ export class ChartManager {
} }
async init() { async init() {
console.log('📊 Initializing Chart Manager...'); console.log('Initializing Chart Manager...');
this.chartsGrid = document.getElementById('chartsGrid'); this.chartsGrid = document.getElementById('chartsGrid');
this.emptyState = document.getElementById('emptyState'); this.emptyState = document.getElementById('emptyState');
if (!this.chartsGrid || !this.emptyState) throw new Error('Required DOM elements not found'); if (!this.chartsGrid || !this.emptyState) throw new Error('Required DOM elements not found');
if (typeof Plotly === 'undefined') throw new Error('Plotly.js not loaded'); if (typeof Plotly === 'undefined') throw new Error('Plotly.js not loaded');
console.log('Chart Manager initialized'); console.log('Chart Manager initialized');
} }
/** Новый входной формат — прямо payload от WS: processor_result */ /** New input format - direct payload from WS: processor_result */
addResult(payload) { addResult(payload) {
try { try {
const { processor_id, timestamp, plotly_config, metadata, data } = payload; const { processor_id, timestamp, plotly_config, metadata, data } = payload;
if (!processor_id) { if (!processor_id) {
console.warn('⚠️ Invalid result - missing processor_id:', payload); console.warn('Invalid result - missing processor_id:', payload);
return; return;
} }
if (this.disabledProcessors.has(processor_id)) { if (this.disabledProcessors.has(processor_id)) {
console.log(`⏸️ Skipping disabled processor: ${processor_id}`); console.log(` Skipping disabled processor: ${processor_id}`);
return; return;
} }
// Store only the latest data (no history needed) // Store only the latest data (no history needed)
this.chartData.set(processor_id, { this.chartData.set(processor_id, {
timestamp: new Date((timestamp ?? Date.now()) * 1000), // если приходит epoch seconds timestamp: new Date((timestamp ?? Date.now()) * 1000), // if timestamp is in epoch seconds
metadata: metadata || {}, metadata: metadata || {},
data: data || {}, data: data || {},
plotly_config: plotly_config || { data: [], layout: {} } plotly_config: plotly_config || { data: [], layout: {} }
@ -103,7 +103,7 @@ export class ChartManager {
this.updateChart(processor_id, plotly_config || { data: [], layout: {} }); this.updateChart(processor_id, plotly_config || { data: [], layout: {} });
this.hideEmptyState(); this.hideEmptyState();
} catch (e) { } catch (e) {
console.error('Error adding chart result:', e); console.error('Error adding chart result:', e);
this.notifications?.show?.({ this.notifications?.show?.({
type: 'error', title: 'Chart Error', message: `Failed to update chart` type: 'error', title: 'Chart Error', message: `Failed to update chart`
}); });
@ -111,7 +111,7 @@ export class ChartManager {
} }
createChart(processorId) { createChart(processorId) {
console.log(`📊 Creating chart for processor: ${processorId}`); console.log(` Creating chart for processor: ${processorId}`);
const card = this.createChartCard(processorId); const card = this.createChartCard(processorId);
this.chartsGrid.appendChild(card); this.chartsGrid.appendChild(card);
@ -144,7 +144,7 @@ export class ChartManager {
if (this.isPaused) return; if (this.isPaused) return;
const chart = this.charts.get(processorId); const chart = this.charts.get(processorId);
if (!chart?.plotContainer) { console.warn(`⚠️ Chart not found for processor: ${processorId}`); return; } if (!chart?.plotContainer) { console.warn(` Chart not found for processor: ${processorId}`); return; }
try { try {
const start = performance.now(); const start = performance.now();
@ -172,7 +172,7 @@ export class ChartManager {
this.updatePerformanceStats(dt); this.updatePerformanceStats(dt);
}); });
} catch (e) { } catch (e) {
console.error(` Error updating chart ${processorId}:`, e); console.error(` Error updating chart ${processorId}:`, e);
} }
} }
@ -187,7 +187,7 @@ export class ChartManager {
while (this.updateQueue.size > 0 && !this.isPaused) { while (this.updateQueue.size > 0 && !this.isPaused) {
const [id, fn] = this.updateQueue.entries().next().value; const [id, fn] = this.updateQueue.entries().next().value;
this.updateQueue.delete(id); this.updateQueue.delete(id);
try { await fn(); } catch (e) { console.error(` Error in queued update for ${id}:`, e); } try { await fn(); } catch (e) { console.error(` Error in queued update for ${id}:`, e); }
await new Promise(r => setTimeout(r, 0)); await new Promise(r => setTimeout(r, 0));
} }
this.isUpdating = false; this.isUpdating = false;
@ -297,11 +297,11 @@ export class ChartManager {
JSON.stringify(uiParameters) !== JSON.stringify(chart.lastUiParameters); JSON.stringify(uiParameters) !== JSON.stringify(chart.lastUiParameters);
if (!parametersChanged) { if (!parametersChanged) {
console.log(` No parameter changes for ${processorId}, skipping settings update`); console.log(` No parameter changes for ${processorId}, skipping settings update`);
return; // No need to update if parameters haven't changed return; // No need to update if parameters haven't changed
} }
console.log(`🔄 Updating settings for ${processorId}:`, { console.log(` Updating settings for ${processorId}:`, {
old: chart.lastUiParameters, old: chart.lastUiParameters,
new: uiParameters new: uiParameters
}); });
@ -341,7 +341,7 @@ export class ChartManager {
uiParameters.forEach(param => { uiParameters.forEach(param => {
const settingKey = `${processorId}_${param.name}`; const settingKey = `${processorId}_${param.name}`;
this.lastSettingValues[settingKey] = param.value; this.lastSettingValues[settingKey] = param.value;
console.log(`💾 Initialized setting value: ${settingKey} = ${param.value}`); console.log(` Initialized setting value: ${settingKey} = ${param.value}`);
}); });
} }
@ -396,7 +396,7 @@ export class ChartManager {
value = value === 'true'; value = value === 'true';
} }
console.log(`🔧 Chart setting changed: ${processorId}.${paramName} = ${value}`); console.log(` Chart setting changed: ${processorId}.${paramName} = ${value}`);
// Store the current setting value to prevent loops // Store the current setting value to prevent loops
if (!this.lastSettingValues) this.lastSettingValues = {}; if (!this.lastSettingValues) this.lastSettingValues = {};
@ -410,7 +410,7 @@ export class ChartManager {
// Check if this is the same value we just set to prevent feedback loop // Check if this is the same value we just set to prevent feedback loop
if (lastValue === value) { if (lastValue === value) {
console.log(`🔄 Skipping duplicate setting: ${settingKey} = ${value} (was ${this.lastSettingValues[settingKey]})`); console.log(` Skipping duplicate setting: ${settingKey} = ${value} (was ${this.lastSettingValues[settingKey]})`);
return; return;
} }
@ -425,13 +425,13 @@ export class ChartManager {
} }
this.settingDebounceTimers[debounceKey] = setTimeout(() => { this.settingDebounceTimers[debounceKey] = setTimeout(() => {
console.log(`📤 Sending setting update: ${processorId}.${paramName} = ${value}`); console.log(` Sending setting update: ${processorId}.${paramName} = ${value}`);
// Send update via WebSocket // Send update via WebSocket
const websocket = window.vnaDashboard?.websocket; const websocket = window.vnaDashboard?.websocket;
if (websocket && websocket.recalculate) { if (websocket && websocket.recalculate) {
websocket.recalculate(processorId, { [paramName]: value }); websocket.recalculate(processorId, { [paramName]: value });
} else { } else {
console.warn('⚠️ WebSocket not available for settings update'); console.warn('WebSocket not available for settings update');
} }
delete this.settingDebounceTimers[debounceKey]; delete this.settingDebounceTimers[debounceKey];
}, 300); // 300ms delay to prevent rapid updates }, 300); // 300ms delay to prevent rapid updates
@ -445,17 +445,17 @@ export class ChartManager {
const paramName = button.dataset.param; const paramName = button.dataset.param;
if (!paramName) { if (!paramName) {
console.warn('⚠️ Button missing param data:', button); console.warn('Button missing param data:', button);
return; return;
} }
// Prevent multiple clicks while processing // Prevent multiple clicks while processing
if (button.disabled) { if (button.disabled) {
console.log('🔘 Button already processing, ignoring click'); console.log('Button already processing, ignoring click');
return; return;
} }
console.log(`🔘 Button clicked: ${processorId}.${paramName}`); console.log(` Button clicked: ${processorId}.${paramName}`);
// Temporarily disable button and show feedback // Temporarily disable button and show feedback
const originalText = button.textContent; const originalText = button.textContent;
@ -465,10 +465,10 @@ export class ChartManager {
// Send button action via WebSocket (set to true to trigger action) - only once // Send button action via WebSocket (set to true to trigger action) - only once
const websocket = window.vnaDashboard?.websocket; const websocket = window.vnaDashboard?.websocket;
if (websocket && websocket.recalculate) { if (websocket && websocket.recalculate) {
console.log(`📤 Sending button action: ${processorId}.${paramName} = true`); console.log(` Sending button action: ${processorId}.${paramName} = true`);
websocket.recalculate(processorId, { [paramName]: true }); websocket.recalculate(processorId, { [paramName]: true });
} else { } else {
console.warn('⚠️ WebSocket not available for button action'); console.warn('WebSocket not available for button action');
} }
// Re-enable button after a short delay // Re-enable button after a short delay
@ -543,7 +543,7 @@ export class ChartManager {
message: `Downloaded ${formatProcessorName(id)} plot and data` message: `Downloaded ${formatProcessorName(id)} plot and data`
}); });
} catch (e) { } catch (e) {
console.error('Chart download failed:', e); console.error('Chart download failed:', e);
this.notifications?.show?.({ this.notifications?.show?.({
type: 'error', type: 'error',
title: 'Download Failed', title: 'Download Failed',
@ -620,7 +620,7 @@ export class ChartManager {
return rawData; return rawData;
} catch (error) { } catch (error) {
console.warn('⚠️ Error extracting processor raw data:', error); console.warn('Error extracting processor raw data:', error);
return { error: 'Failed to extract raw data' }; return { error: 'Failed to extract raw data' };
} }
} }
@ -681,8 +681,8 @@ export class ChartManager {
this.performanceStats.avgUpdateTime = total / this.performanceStats.updatesProcessed; this.performanceStats.avgUpdateTime = total / this.performanceStats.updatesProcessed;
} }
pause() { this.isPaused = true; console.log('⏸️ Chart updates paused'); } pause() { this.isPaused = true; console.log('Chart updates paused'); }
resume() { this.isPaused = false; console.log('▶️ Chart updates resumed'); if (this.updateQueue.size) this.processUpdateQueue(); } resume() { this.isPaused = false; console.log('Chart updates resumed'); if (this.updateQueue.size) this.processUpdateQueue(); }
/** /**
@ -706,7 +706,7 @@ export class ChartManager {
} }
}; };
} catch (error) { } catch (error) {
console.warn(`⚠️ Could not extract Plotly data for ${processorId}:`, error); console.warn(` Could not extract Plotly data for ${processorId}:`, error);
return null; return null;
} }
} }
@ -760,7 +760,7 @@ export class ChartManager {
return { data, layout }; return { data, layout };
} catch (error) { } catch (error) {
console.warn(`⚠️ Could not extract safe Plotly data for ${processorId}:`, error); console.warn(` Could not extract safe Plotly data for ${processorId}:`, error);
return null; return null;
} }
} }
@ -790,11 +790,11 @@ export class ChartManager {
} }
destroy() { destroy() {
console.log('🧹 Cleaning up Chart Manager...'); console.log('Cleaning up Chart Manager...');
this.clearAll(); this.clearAll();
this.updateQueue.clear(); this.updateQueue.clear();
this.isUpdating = false; this.isUpdating = false;
this.isPaused = true; this.isPaused = true;
console.log('Chart Manager cleanup complete'); console.log('Chart Manager cleanup complete');
} }
} }

View File

@ -59,7 +59,7 @@ export class NotificationManager {
document.body.appendChild(this.container); document.body.appendChild(this.container);
} }
console.log('📢 Notification Manager initialized'); console.log('Notification Manager initialized');
} }
/** /**
@ -68,7 +68,7 @@ export class NotificationManager {
show(options) { show(options) {
// Validate options // Validate options
if (!options || typeof options !== 'object') { if (!options || typeof options !== 'object') {
console.warn('⚠️ Invalid notification options:', options); console.warn('Invalid notification options:', options);
return null; return null;
} }
@ -76,7 +76,7 @@ export class NotificationManager {
// Don't show notification if both title and message are empty // Don't show notification if both title and message are empty
if (!title && !message) { if (!title && !message) {
console.warn('⚠️ Skipping notification with empty title and message'); console.warn('Skipping notification with empty title and message');
return null; return null;
} }
@ -86,7 +86,7 @@ export class NotificationManager {
const lastShown = this.recentNotifications.get(notificationKey); const lastShown = this.recentNotifications.get(notificationKey);
if (lastShown && (now - lastShown) < 2000) { if (lastShown && (now - lastShown) < 2000) {
console.log('🔄 Skipping duplicate notification:', notificationKey); console.log('Skipping duplicate notification:', notificationKey);
return null; // Skip duplicate notification return null; // Skip duplicate notification
} }
@ -169,7 +169,7 @@ export class NotificationManager {
lucide.createIcons({ attrs: { 'stroke-width': 1.5 } }); lucide.createIcons({ attrs: { 'stroke-width': 1.5 } });
} }
console.log(`📢 Showing ${notification.type} notification:`, notification.title); console.log(` Showing ${notification.type} notification:`, notification.title);
} }
/** /**
@ -249,7 +249,7 @@ export class NotificationManager {
try { try {
actionConfig.handler(notification); actionConfig.handler(notification);
} catch (error) { } catch (error) {
console.error('Error in notification action handler:', error); console.error('Error in notification action handler:', error);
} }
} }
@ -285,7 +285,7 @@ export class NotificationManager {
const notification = this.notifications.get(id); const notification = this.notifications.get(id);
if (!notification) return; if (!notification) return;
console.log(`📢 Dismissing notification: ${id}`); console.log(` Dismissing notification: ${id}`);
// Clear timer // Clear timer
if (notification.timer) { if (notification.timer) {
@ -312,7 +312,7 @@ export class NotificationManager {
* Dismiss all notifications * Dismiss all notifications
*/ */
dismissAll() { dismissAll() {
console.log('📢 Dismissing all notifications'); console.log('Dismissing all notifications');
for (const id of this.notifications.keys()) { for (const id of this.notifications.keys()) {
this.dismiss(id); this.dismiss(id);
@ -323,7 +323,7 @@ export class NotificationManager {
* Dismiss notifications by type * Dismiss notifications by type
*/ */
dismissByType(type) { dismissByType(type) {
console.log(`📢 Dismissing all ${type} notifications`); console.log(` Dismissing all ${type} notifications`);
for (const notification of this.notifications.values()) { for (const notification of this.notifications.values()) {
if (notification.type === type) { if (notification.type === type) {
@ -487,7 +487,7 @@ export class NotificationManager {
* Cleanup * Cleanup
*/ */
destroy() { destroy() {
console.log('🧹 Cleaning up Notification Manager...'); console.log('Cleaning up Notification Manager...');
this.dismissAll(); this.dismissAll();
@ -495,6 +495,6 @@ export class NotificationManager {
this.container.parentNode.removeChild(this.container); this.container.parentNode.removeChild(this.container);
} }
console.log('Notification Manager cleanup complete'); console.log('Notification Manager cleanup complete');
} }
} }

View File

@ -71,9 +71,9 @@ export class SettingsManager {
await this._loadInitialData(); await this._loadInitialData();
this.isInitialized = true; this.isInitialized = true;
console.log('Settings Manager initialized'); console.log('Settings Manager initialized');
} catch (err) { } catch (err) {
console.error('Settings Manager init failed:', err); console.error('Settings Manager init failed:', err);
this._notify('error', 'Settings Error', 'Failed to initialize settings'); this._notify('error', 'Settings Error', 'Failed to initialize settings');
} }
} }
@ -83,7 +83,7 @@ export class SettingsManager {
this._resetCalibrationCaptureState(); this._resetCalibrationCaptureState();
this._detachEvents(); this._detachEvents();
this.isInitialized = false; this.isInitialized = false;
console.log('🧹 Settings Manager destroyed'); console.log('Settings Manager destroyed');
} }
async refresh() { async refresh() {
@ -287,7 +287,7 @@ export class SettingsManager {
calibrations.forEach(c => { calibrations.forEach(c => {
const opt = document.createElement('option'); const opt = document.createElement('option');
opt.value = c.name; opt.value = c.name;
opt.textContent = `${c.name} ${c.is_complete ? '' : ''}`; opt.textContent = `${c.name} ${c.is_complete ? '' : '?'}`;
dd.appendChild(opt); dd.appendChild(opt);
}); });
@ -392,7 +392,7 @@ export class SettingsManager {
btn.title = 'Standard is currently being captured'; btn.title = 'Standard is currently being captured';
} else if (isCompleted) { } else if (isCompleted) {
btn.classList.add('btn--success'); btn.classList.add('btn--success');
btn.innerHTML = `<i data-lucide="check"></i> ${std.toUpperCase()}`; btn.innerHTML = `<i data-lucide="check"></i> ${std.toUpperCase()}`;
btn.disabled = false; btn.disabled = false;
btn.title = 'Click to recapture this standard'; btn.title = 'Click to recapture this standard';
} else if (isMissing) { } else if (isMissing) {
@ -435,7 +435,7 @@ export class SettingsManager {
// Reset reference state // Reset reference state
this.availableReferences = []; this.availableReferences = [];
this.currentReference = null; this.currentReference = null;
console.log('🔄 Calibration and reference UI reset after preset change'); console.log('Calibration and reference UI reset after preset change');
} }
/* ----------------------------- Event Handlers (UI) ----------------------------- */ /* ----------------------------- Event Handlers (UI) ----------------------------- */
@ -470,10 +470,10 @@ export class SettingsManager {
this._notify('success', 'Preset Set', result.message); this._notify('success', 'Preset Set', result.message);
// Сброс UI калибровки // Reset calibration UI
this._resetCalibrationStateForPresetChange(); this._resetCalibrationStateForPresetChange();
// Обновить статус // Update status
await this._loadStatus(); await this._loadStatus();
} catch (e) { } catch (e) {
console.error('Set preset failed:', e); console.error('Set preset failed:', e);
@ -521,7 +521,7 @@ export class SettingsManager {
this.debouncer.debounce(key, () => this.debouncer.debounce(key, () =>
this.reqGuard.runExclusive(key, async () => { this.reqGuard.runExclusive(key, async () => {
try { try {
// Отметим стандарт как «занят» // Mark standard as busy
this.disabledStandards.add(standard); this.disabledStandards.add(standard);
const btn = document.querySelector(`[data-standard="${standard}"]`); const btn = document.querySelector(`[data-standard="${standard}"]`);
@ -695,13 +695,13 @@ export class SettingsManager {
const modal = this.elements.plotsModal; const modal = this.elements.plotsModal;
if (!modal) return; if (!modal) return;
// Запомним пакет // Store data package
this.currentPlotsData = plotsData; this.currentPlotsData = plotsData;
// Рендер карточек/графиков // Render cards/plots
this._renderCalibrationPlots(plotsData.individual_plots, plotsData.preset); this._renderCalibrationPlots(plotsData.individual_plots, plotsData.preset);
// Заголовок // Header
const title = modal.querySelector('.modal__title'); const title = modal.querySelector('.modal__title');
if (title) { if (title) {
title.innerHTML = ` title.innerHTML = `
@ -711,10 +711,10 @@ export class SettingsManager {
if (typeof lucide !== 'undefined') lucide.createIcons(); if (typeof lucide !== 'undefined') lucide.createIcons();
} }
// Кнопки закрытия/скачивания // Close/download buttons
this._setupModalCloseHandlers(modal); this._setupModalCloseHandlers(modal);
// Показ // Display
modal.classList.add('modal--active'); modal.classList.add('modal--active');
document.body.style.overflow = 'hidden'; document.body.style.overflow = 'hidden';
} }

View File

@ -16,7 +16,7 @@ export class StorageManager {
debug: 'debug_info' debug: 'debug_info'
}; };
console.log(`💾 Storage Manager initialized (${this.isAvailable ? 'available' : 'not available'})`); console.log(` Storage Manager initialized (${this.isAvailable ? 'available' : 'not available'})`);
} }
/** /**
@ -29,7 +29,7 @@ export class StorageManager {
localStorage.removeItem(test); localStorage.removeItem(test);
return true; return true;
} catch (e) { } catch (e) {
console.warn('⚠️ localStorage not available:', e.message); console.warn('localStorage not available:', e.message);
return false; return false;
} }
} }
@ -46,7 +46,7 @@ export class StorageManager {
*/ */
async save(key, data) { async save(key, data) {
if (!this.isAvailable) { if (!this.isAvailable) {
console.warn('⚠️ Storage not available, cannot save:', key); console.warn('Storage not available, cannot save:', key);
return false; return false;
} }
@ -58,24 +58,24 @@ export class StorageManager {
}); });
localStorage.setItem(this.getKey(key), serialized); localStorage.setItem(this.getKey(key), serialized);
console.log(`💾 Saved to storage: ${key}`); console.log(` Saved to storage: ${key}`);
return true; return true;
} catch (error) { } catch (error) {
console.error('Failed to save to storage:', error); console.error('Failed to save to storage:', error);
// Handle quota exceeded error // Handle quota exceeded error
if (error.name === 'QuotaExceededError') { if (error.name === 'QuotaExceededError') {
console.log('🧹 Storage quota exceeded, cleaning up...'); console.log('Storage quota exceeded, cleaning up...');
this.cleanup(); this.cleanup();
// Try again after cleanup // Try again after cleanup
try { try {
localStorage.setItem(this.getKey(key), serialized); localStorage.setItem(this.getKey(key), serialized);
console.log(`💾 Saved to storage after cleanup: ${key}`); console.log(` Saved to storage after cleanup: ${key}`);
return true; return true;
} catch (retryError) { } catch (retryError) {
console.error('Still failed after cleanup:', retryError); console.error('Still failed after cleanup:', retryError);
} }
} }
@ -88,7 +88,7 @@ export class StorageManager {
*/ */
async load(key) { async load(key) {
if (!this.isAvailable) { if (!this.isAvailable) {
console.warn('⚠️ Storage not available, cannot load:', key); console.warn('Storage not available, cannot load:', key);
return null; return null;
} }
@ -102,16 +102,16 @@ export class StorageManager {
// Validate structure // Validate structure
if (!parsed.data || !parsed.timestamp) { if (!parsed.data || !parsed.timestamp) {
console.warn('⚠️ Invalid storage data format:', key); console.warn('Invalid storage data format:', key);
this.remove(key); // Clean up invalid data this.remove(key); // Clean up invalid data
return null; return null;
} }
console.log(`💾 Loaded from storage: ${key} (${new Date(parsed.timestamp).toLocaleString()})`); console.log(` Loaded from storage: ${key} (${new Date(parsed.timestamp).toLocaleString()})`);
return parsed.data; return parsed.data;
} catch (error) { } catch (error) {
console.error('Failed to load from storage:', error); console.error('Failed to load from storage:', error);
this.remove(key); // Clean up corrupted data this.remove(key); // Clean up corrupted data
return null; return null;
} }
@ -125,10 +125,10 @@ export class StorageManager {
try { try {
localStorage.removeItem(this.getKey(key)); localStorage.removeItem(this.getKey(key));
console.log(`🗑️ Removed from storage: ${key}`); console.log(` Removed from storage: ${key}`);
return true; return true;
} catch (error) { } catch (error) {
console.error('Failed to remove from storage:', error); console.error('Failed to remove from storage:', error);
return false; return false;
} }
} }
@ -142,7 +142,7 @@ export class StorageManager {
try { try {
return localStorage.getItem(this.getKey(key)) !== null; return localStorage.getItem(this.getKey(key)) !== null;
} catch (error) { } catch (error) {
console.error('Failed to check storage existence:', error); console.error('Failed to check storage existence:', error);
return false; return false;
} }
} }
@ -215,7 +215,7 @@ export class StorageManager {
if (data && data.expiresAt) { if (data && data.expiresAt) {
if (Date.now() > data.expiresAt) { if (Date.now() > data.expiresAt) {
console.log('🕒 Session data expired, removing...'); console.log('Session data expired, removing...');
await this.remove(this.keys.session); await this.remove(this.keys.session);
return null; return null;
} }
@ -278,7 +278,7 @@ export class StorageManager {
}; };
} catch (error) { } catch (error) {
console.error('Failed to calculate storage stats:', error); console.error('Failed to calculate storage stats:', error);
return { available: false, error: error.message }; return { available: false, error: error.message };
} }
} }
@ -310,7 +310,7 @@ export class StorageManager {
async cleanup() { async cleanup() {
if (!this.isAvailable) return; if (!this.isAvailable) return;
console.log('🧹 Cleaning up storage...'); console.log('Cleaning up storage...');
try { try {
const keysToRemove = []; const keysToRemove = [];
@ -339,13 +339,13 @@ export class StorageManager {
// Remove identified keys // Remove identified keys
keysToRemove.forEach(key => { keysToRemove.forEach(key => {
localStorage.removeItem(key); localStorage.removeItem(key);
console.log(`🗑️ Cleaned up: ${key}`); console.log(` Cleaned up: ${key}`);
}); });
console.log(`🧹 Cleanup complete. Removed ${keysToRemove.length} items.`); console.log(` Cleanup complete. Removed ${keysToRemove.length} items.`);
} catch (error) { } catch (error) {
console.error('Cleanup failed:', error); console.error('Cleanup failed:', error);
} }
} }
@ -355,7 +355,7 @@ export class StorageManager {
async clearAll() { async clearAll() {
if (!this.isAvailable) return false; if (!this.isAvailable) return false;
console.log('🗑️ Clearing all storage data...'); console.log('Clearing all storage data...');
try { try {
const keysToRemove = []; const keysToRemove = [];
@ -373,11 +373,11 @@ export class StorageManager {
localStorage.removeItem(key); localStorage.removeItem(key);
}); });
console.log(`🗑️ Cleared ${keysToRemove.length} items from storage`); console.log(` Cleared ${keysToRemove.length} items from storage`);
return true; return true;
} catch (error) { } catch (error) {
console.error('Failed to clear storage:', error); console.error('Failed to clear storage:', error);
return false; return false;
} }
} }
@ -405,11 +405,11 @@ export class StorageManager {
} }
} }
console.log('📤 Exported storage data'); console.log('Exported storage data');
return exportData; return exportData;
} catch (error) { } catch (error) {
console.error('Failed to export data:', error); console.error('Failed to export data:', error);
return null; return null;
} }
} }
@ -431,11 +431,11 @@ export class StorageManager {
localStorage.setItem(fullKey, JSON.stringify(value)); localStorage.setItem(fullKey, JSON.stringify(value));
} }
console.log(`📥 Imported ${Object.keys(importData.data).length} items`); console.log(` Imported ${Object.keys(importData.data).length} items`);
return true; return true;
} catch (error) { } catch (error) {
console.error('Failed to import data:', error); console.error('Failed to import data:', error);
return false; return false;
} }
} }
@ -444,10 +444,10 @@ export class StorageManager {
* Cleanup on destroy * Cleanup on destroy
*/ */
destroy() { destroy() {
console.log('🧹 Storage Manager cleanup...'); console.log('Storage Manager cleanup...');
// Perform any necessary cleanup // Perform any necessary cleanup
// For now, just run a cleanup to free up space // For now, just run a cleanup to free up space
this.cleanup(); this.cleanup();
console.log('Storage Manager cleanup complete'); console.log('Storage Manager cleanup complete');
} }
} }

View File

@ -38,7 +38,7 @@ export class UIManager {
* Initialize UI Manager * Initialize UI Manager
*/ */
async init() { async init() {
console.log('🎨 Initializing UI Manager...'); console.log('Initializing UI Manager...');
// Get DOM elements // Get DOM elements
this.findElements(); this.findElements();
@ -59,7 +59,7 @@ export class UIManager {
this.websocket.on('processor_result', (payload) => this.onProcessorResult(payload)); this.websocket.on('processor_result', (payload) => this.onProcessorResult(payload));
} }
console.log('UI Manager initialized'); console.log('UI Manager initialized');
} }
/** /**
@ -263,12 +263,12 @@ export class UIManager {
} }
handleResize() { handleResize() {
console.log('📱 Window resized'); console.log('Window resized');
// Charts handle their own resize // Charts handle their own resize
} }
// System status and theme methods simplified // System status and theme methods simplified
updateSystemStatus(statusData) { console.log('📊 System status:', statusData); } updateSystemStatus(statusData) { console.log('System status:', statusData); }
setTheme(theme) { document.documentElement.setAttribute('data-theme', theme); } setTheme(theme) { document.documentElement.setAttribute('data-theme', theme); }
getCurrentTheme() { return document.documentElement.getAttribute('data-theme') || 'dark'; } getCurrentTheme() { return document.documentElement.getAttribute('data-theme') || 'dark'; }
@ -280,7 +280,7 @@ export class UIManager {
if (this.eventHandlers[eventType]) { if (this.eventHandlers[eventType]) {
this.eventHandlers[eventType].forEach(handler => { this.eventHandlers[eventType].forEach(handler => {
try { handler(...args); } try { handler(...args); }
catch (error) { console.error(` Error in ${eventType} handler:`, error); } catch (error) { console.error(` Error in ${eventType} handler:`, error); }
}); });
} }
} }
@ -294,7 +294,7 @@ export class UIManager {
} }
destroy() { destroy() {
console.log('🧹 Cleaning up UI Manager...'); console.log('Cleaning up UI Manager...');
// Clear event handlers // Clear event handlers
Object.keys(this.eventHandlers).forEach(key => { Object.keys(this.eventHandlers).forEach(key => {
@ -304,6 +304,6 @@ export class UIManager {
// Clear processors // Clear processors
this.processors.clear(); this.processors.clear();
console.log('UI Manager cleanup complete'); console.log('UI Manager cleanup complete');
} }
} }

View File

@ -120,47 +120,21 @@ export class ButtonState {
} }
/** /**
* Set button to loading state with animation * Set button to disabled state during request
* @param {HTMLElement} button - Button element * @param {HTMLElement} button - Button element
* @param {boolean} loading - Whether to set loading state * @param {boolean} loading - Whether to disable button
* @param {Object} originalState - Original button state to restore * @param {Object} originalState - Original button state to restore
*/ */
export function setButtonLoading(button, loading, originalState = {}) { export function setButtonLoading(button, loading, originalState = {}) {
if (!button) return; if (!button) return originalState;
if (loading) { if (loading) {
// Save original state if not provided if (!originalState.disabled) {
if (!originalState.text) { originalState.disabled = button.disabled;
originalState.text = button.textContent;
originalState.icon = button.querySelector('i')?.getAttribute('data-lucide');
} }
button.disabled = true; 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';
}
if (window.lucide) {
window.lucide.createIcons();
}
} else { } else {
button.disabled = false; button.disabled = originalState.disabled || false;
button.classList.remove('loading');
const icon = button.querySelector('i');
if (icon) {
icon.style.animation = '';
if (originalState.icon) {
icon.setAttribute('data-lucide', originalState.icon);
if (window.lucide) {
window.lucide.createIcons();
}
}
}
} }
return originalState; return originalState;

View File

@ -33,7 +33,7 @@ export class WebSocketManager {
async connect() { async connect() {
if (this.isConnected || this.isConnecting) { if (this.isConnected || this.isConnecting) {
console.log('🔌 WebSocket already connected/connecting'); console.log('WebSocket already connected/connecting');
return; return;
} }
@ -41,7 +41,7 @@ export class WebSocketManager {
this.isConnecting = true; this.isConnecting = true;
this.emit('connecting'); this.emit('connecting');
console.log(`🔌 Connecting to WebSocket: ${this.config.url}`); console.log(` Connecting to WebSocket: ${this.config.url}`);
this.ws = new WebSocket(this.config.url); this.ws = new WebSocket(this.config.url);
this.setupWebSocketEvents(); this.setupWebSocketEvents();
@ -49,7 +49,7 @@ export class WebSocketManager {
await this.waitForConnection(5000); await this.waitForConnection(5000);
} catch (error) { } catch (error) {
this.isConnecting = false; this.isConnecting = false;
console.error('WebSocket connection failed:', error); console.error('WebSocket connection failed:', error);
this.handleConnectionError(error); this.handleConnectionError(error);
throw error; throw error;
} }
@ -85,7 +85,7 @@ export class WebSocketManager {
if (!this.ws) return; if (!this.ws) return;
this.ws.onopen = (event) => { this.ws.onopen = (event) => {
console.log('WebSocket connected'); console.log('WebSocket connected');
this.isConnected = true; this.isConnected = true;
this.isConnecting = false; this.isConnecting = false;
this.reconnectAttempts = 0; this.reconnectAttempts = 0;
@ -106,7 +106,7 @@ export class WebSocketManager {
try { try {
this.handleMessage(event.data); this.handleMessage(event.data);
} catch (error) { } catch (error) {
console.error('Error processing WebSocket message:', error); console.error('Error processing WebSocket message:', error);
this.notifications?.show?.({ this.notifications?.show?.({
type: 'error', type: 'error',
title: 'Message Error', title: 'Message Error',
@ -116,12 +116,12 @@ export class WebSocketManager {
}; };
this.ws.onerror = (error) => { this.ws.onerror = (error) => {
console.error('WebSocket error:', error); console.error('WebSocket error:', error);
this.handleConnectionError(error); this.handleConnectionError(error);
}; };
this.ws.onclose = (event) => { this.ws.onclose = (event) => {
console.log('🔌 WebSocket closed:', event.code, event.reason); console.log('WebSocket closed:', event.code, event.reason);
this.handleDisconnection(event); this.handleDisconnection(event);
}; };
} }
@ -134,7 +134,7 @@ export class WebSocketManager {
try { try {
if (typeof data !== 'string') { if (typeof data !== 'string') {
console.warn('⚠️ Received non-text message, ignoring'); console.warn('Received non-text message, ignoring');
return; return;
} }
if (data === 'ping') { this.handlePing(); return; } if (data === 'ping') { this.handlePing(); return; }
@ -144,7 +144,7 @@ export class WebSocketManager {
try { try {
payload = JSON.parse(data); payload = JSON.parse(data);
} catch (jsonError) { } catch (jsonError) {
console.error('Invalid JSON format:', data); console.error('Invalid JSON format:', data);
console.error('JSON parse error:', jsonError); console.error('JSON parse error:', jsonError);
this.notifications?.show?.({ this.notifications?.show?.({
type: 'error', type: 'error',
@ -154,7 +154,7 @@ export class WebSocketManager {
return; return;
} }
// Публичное событие «data» — для всего, плюс более точечные: // Public "data" event for everything, plus more specific ones:
this.emit('data', payload); this.emit('data', payload);
switch (payload.type) { switch (payload.type) {
@ -165,8 +165,8 @@ export class WebSocketManager {
this.emit('processor_history', payload); this.emit('processor_history', payload);
break; break;
case 'error': case 'error':
console.error('🔴 Server error:', payload); console.error('Server error:', payload);
console.error('🔴 Error details:', { console.error('Error details:', {
message: payload.message, message: payload.message,
code: payload.code, code: payload.code,
details: payload.details, details: payload.details,
@ -180,11 +180,11 @@ export class WebSocketManager {
}); });
break; break;
default: default:
console.warn('⚠️ Unknown payload type:', payload.type); console.warn('Unknown payload type:', payload.type);
} }
} catch (e) { } catch (e) {
console.error('Failed to parse WebSocket JSON:', e); console.error('Failed to parse WebSocket JSON:', e);
console.log('📝 Raw message:', data); console.log('Raw message:', data);
this.notifications?.show?.({ this.notifications?.show?.({
type: 'error', type: 'error',
title: 'Message Processing Error', title: 'Message Processing Error',
@ -205,7 +205,7 @@ export class WebSocketManager {
} }
handlePong() { handlePong() {
if (this.lastPing) { if (this.lastPing) {
console.log(`🏓 WebSocket latency: ${Date.now() - this.lastPing}ms`); console.log(` WebSocket latency: ${Date.now() - this.lastPing}ms`);
this.lastPing = null; this.lastPing = null;
} }
} }
@ -251,7 +251,7 @@ export class WebSocketManager {
scheduleReconnect() { scheduleReconnect() {
if (this.reconnectTimer) return; if (this.reconnectTimer) return;
const delay = Math.min(this.config.reconnectInterval * Math.pow(2, this.reconnectAttempts), 30000); const delay = Math.min(this.config.reconnectInterval * Math.pow(2, this.reconnectAttempts), 30000);
console.log(`🔄 Scheduling reconnect in ${delay}ms (attempt ${this.reconnectAttempts + 1})`); console.log(` Scheduling reconnect in ${delay}ms (attempt ${this.reconnectAttempts + 1})`);
this.reconnectTimer = setTimeout(() => { this.reconnectTimer = setTimeout(() => {
this.reconnectTimer = null; this.reconnectTimer = null;
this.reconnect(); this.reconnect();
@ -260,7 +260,7 @@ export class WebSocketManager {
async reconnect() { async reconnect() {
if (this.reconnectAttempts >= this.config.maxReconnectAttempts) { if (this.reconnectAttempts >= this.config.maxReconnectAttempts) {
console.error('Max reconnection attempts reached'); console.error('Max reconnection attempts reached');
this.notifications?.show?.({ this.notifications?.show?.({
type: 'error', type: 'error',
title: 'Connection Failed', title: 'Connection Failed',
@ -271,16 +271,16 @@ export class WebSocketManager {
this.reconnectAttempts++; this.reconnectAttempts++;
if (this.ws) { this.ws.close(); this.ws = null; } if (this.ws) { this.ws.close(); this.ws = null; }
try { await this.connect(); } try { await this.connect(); }
catch (error) { console.error(` Reconnection attempt ${this.reconnectAttempts} failed:`, error); this.scheduleReconnect(); } catch (error) { console.error(` Reconnection attempt ${this.reconnectAttempts} failed:`, error); this.scheduleReconnect(); }
} }
send(data) { send(data) {
if (!this.isConnected || !this.ws) { if (!this.isConnected || !this.ws) {
if (this.messageQueue.length < this.maxQueueSize) { if (this.messageQueue.length < this.maxQueueSize) {
this.messageQueue.push(data); this.messageQueue.push(data);
console.log('📤 Message queued (not connected)'); console.log('Message queued (not connected)');
} else { } else {
console.warn('⚠️ Message queue full, dropping message'); console.warn('Message queue full, dropping message');
} }
return false; return false;
} }
@ -289,22 +289,22 @@ export class WebSocketManager {
this.ws.send(msg); this.ws.send(msg);
return true; return true;
} catch (e) { } catch (e) {
console.error('Failed to send message:', e); console.error('Failed to send message:', e);
return false; return false;
} }
} }
processPendingMessages() { processPendingMessages() {
if (this.messageQueue.length === 0) return; if (this.messageQueue.length === 0) return;
console.log(`📤 Processing ${this.messageQueue.length} queued messages`); console.log(` Processing ${this.messageQueue.length} queued messages`);
const messages = [...this.messageQueue]; const messages = [...this.messageQueue];
this.messageQueue = []; this.messageQueue = [];
messages.forEach(m => this.send(m)); messages.forEach(m => this.send(m));
} }
// === ПУБЛИЧНОЕ API ДЛЯ СОВМЕСТИМОСТИ С БЭКЕНДОМ === // === PUBLIC API FOR BACKEND COMPATIBILITY ===
/** Запросить пересчёт с обновлением конфигурации (СУЩЕСТВУЕТ НА БЭКЕНДЕ) */ /** Request recalculation with config update (EXISTS ON BACKEND) */
recalculate(processorId, configUpdates = undefined) { recalculate(processorId, configUpdates = undefined) {
return this.send({ return this.send({
type: 'recalculate', type: 'recalculate',
@ -313,7 +313,7 @@ export class WebSocketManager {
}); });
} }
/** Получить историю результатов процессора (СУЩЕСТВУЕТ НА БЭКЕНДЕ) */ /** Get processor results history (EXISTS ON BACKEND) */
getHistory(processorId, limit = 10) { getHistory(processorId, limit = 10) {
return this.send({ return this.send({
type: 'get_history', type: 'get_history',
@ -336,12 +336,12 @@ export class WebSocketManager {
emit(event, data) { emit(event, data) {
if (!this.eventListeners.has(event)) return; if (!this.eventListeners.has(event)) return;
this.eventListeners.get(event).forEach(cb => { this.eventListeners.get(event).forEach(cb => {
try { cb(data); } catch (e) { console.error(` Error in event listener for ${event}:`, e); } try { cb(data); } catch (e) { console.error(` Error in event listener for ${event}:`, e); }
}); });
} }
disconnect() { disconnect() {
console.log('🔌 Disconnecting WebSocket'); console.log('Disconnecting WebSocket');
if (this.reconnectTimer) { clearTimeout(this.reconnectTimer); this.reconnectTimer = null; } if (this.reconnectTimer) { clearTimeout(this.reconnectTimer); this.reconnectTimer = null; }
if (this.pingTimer) { clearInterval(this.pingTimer); this.pingTimer = null; } if (this.pingTimer) { clearInterval(this.pingTimer); this.pingTimer = null; }
if (this.ws) { this.ws.close(1000, 'Manual disconnect'); this.ws = null; } if (this.ws) { this.ws.close(1000, 'Manual disconnect'); this.ws = null; }