refactor(components): split components.js into separate files and add loader; app waits for components before init

This commit is contained in:
2025-08-31 14:00:33 +02:00
parent 83d252f3cc
commit cc7fa0fa00
11 changed files with 3124 additions and 2 deletions

View File

@@ -0,0 +1,90 @@
// Primary Node Component
class PrimaryNodeComponent extends Component {
constructor(container, viewModel, eventBus) {
super(container, viewModel, eventBus);
}
setupEventListeners() {
const refreshBtn = this.findElement('.primary-node-refresh');
if (refreshBtn) {
this.addEventListener(refreshBtn, 'click', this.handleRandomSelection.bind(this));
}
}
setupViewModelListeners() {
// Listen to primary node changes
this.subscribeToProperty('primaryNode', this.render.bind(this));
this.subscribeToProperty('clientInitialized', this.render.bind(this));
this.subscribeToProperty('totalNodes', this.render.bind(this));
this.subscribeToProperty('onlineNodes', this.render.bind(this));
this.subscribeToProperty('error', this.render.bind(this));
}
render() {
const primaryNode = this.viewModel.get('primaryNode');
const clientInitialized = this.viewModel.get('clientInitialized');
const totalNodes = this.viewModel.get('totalNodes');
const onlineNodes = this.viewModel.get('onlineNodes');
const error = this.viewModel.get('error');
if (error) {
this.setText('#primary-node-ip', '❌ Discovery Failed');
this.setClass('#primary-node-ip', 'error', true);
this.setClass('#primary-node-ip', 'discovering', false);
this.setClass('#primary-node-ip', 'selecting', false);
return;
}
if (!primaryNode) {
this.setText('#primary-node-ip', '🔍 No Nodes Found');
this.setClass('#primary-node-ip', 'error', true);
this.setClass('#primary-node-ip', 'discovering', false);
this.setClass('#primary-node-ip', 'selecting', false);
return;
}
const status = clientInitialized ? '✅' : '⚠️';
const nodeCount = (onlineNodes && onlineNodes > 0)
? ` (${onlineNodes}/${totalNodes} online)`
: (totalNodes > 1 ? ` (${totalNodes} nodes)` : '');
this.setText('#primary-node-ip', `${status} ${primaryNode}${nodeCount}`);
this.setClass('#primary-node-ip', 'error', false);
this.setClass('#primary-node-ip', 'discovering', false);
this.setClass('#primary-node-ip', 'selecting', false);
}
async handleRandomSelection() {
try {
// Show selecting state
this.setText('#primary-node-ip', '🎲 Selecting...');
this.setClass('#primary-node-ip', 'selecting', true);
this.setClass('#primary-node-ip', 'discovering', false);
this.setClass('#primary-node-ip', 'error', false);
await this.viewModel.selectRandomPrimaryNode();
// Show success briefly
this.setText('#primary-node-ip', '🎯 Selection Complete');
// Update display after delay
setTimeout(() => {
this.viewModel.updatePrimaryNodeDisplay();
}, 1500);
} catch (error) {
logger.error('Failed to select random primary node:', error);
this.setText('#primary-node-ip', '❌ Selection Failed');
this.setClass('#primary-node-ip', 'error', true);
this.setClass('#primary-node-ip', 'selecting', false);
this.setClass('#primary-node-ip', 'discovering', false);
// Revert to normal display after error
setTimeout(() => {
this.viewModel.updatePrimaryNodeDisplay();
}, 2000);
}
}
}
window.PrimaryNodeComponent = PrimaryNodeComponent;