feat: add fw tab
This commit is contained in:
151
public/script.js
151
public/script.js
@@ -529,8 +529,157 @@ function displayClusterMembers(members, expandedCards = new Map()) {
|
||||
// Load cluster members when page loads
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
refreshClusterMembers();
|
||||
setupNavigation();
|
||||
setupFirmwareView();
|
||||
});
|
||||
|
||||
// Auto-refresh every 30 seconds
|
||||
// FIXME not working properly: scroll position is not preserved, if there is an upload happening, this mus also be handled
|
||||
//setInterval(refreshClusterMembers, 30000);
|
||||
//setInterval(refreshClusterMembers, 30000);
|
||||
|
||||
// Setup navigation menu
|
||||
function setupNavigation() {
|
||||
const navTabs = document.querySelectorAll('.nav-tab');
|
||||
const viewContents = document.querySelectorAll('.view-content');
|
||||
|
||||
navTabs.forEach(tab => {
|
||||
tab.addEventListener('click', () => {
|
||||
const targetView = tab.dataset.view;
|
||||
|
||||
// Update active tab
|
||||
navTabs.forEach(t => t.classList.remove('active'));
|
||||
tab.classList.add('active');
|
||||
|
||||
// Update active view
|
||||
viewContents.forEach(view => view.classList.remove('active'));
|
||||
const targetViewElement = document.getElementById(`${targetView}-view`);
|
||||
if (targetViewElement) {
|
||||
targetViewElement.classList.add('active');
|
||||
}
|
||||
|
||||
// Refresh the active view
|
||||
if (targetView === 'cluster') {
|
||||
refreshClusterMembers();
|
||||
} else if (targetView === 'firmware') {
|
||||
refreshFirmwareView();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Setup firmware view
|
||||
function setupFirmwareView() {
|
||||
// Setup global firmware file input
|
||||
const globalFirmwareFile = document.getElementById('global-firmware-file');
|
||||
if (globalFirmwareFile) {
|
||||
globalFirmwareFile.addEventListener('change', handleGlobalFirmwareUpload);
|
||||
}
|
||||
|
||||
// Setup target selection
|
||||
const targetRadios = document.querySelectorAll('input[name="target-type"]');
|
||||
const specificNodeSelect = document.getElementById('specific-node-select');
|
||||
|
||||
targetRadios.forEach(radio => {
|
||||
radio.addEventListener('change', () => {
|
||||
if (radio.value === 'specific') {
|
||||
specificNodeSelect.style.display = 'block';
|
||||
populateNodeSelect();
|
||||
} else {
|
||||
specificNodeSelect.style.display = 'none';
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Handle global firmware upload
|
||||
async function handleGlobalFirmwareUpload(event) {
|
||||
const file = event.target.files[0];
|
||||
if (!file) return;
|
||||
|
||||
const targetType = document.querySelector('input[name="target-type"]:checked').value;
|
||||
const specificNode = document.getElementById('specific-node-select').value;
|
||||
|
||||
if (targetType === 'specific' && !specificNode) {
|
||||
alert('Please select a specific node to update.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (targetType === 'all') {
|
||||
await uploadFirmwareToAllNodes(file);
|
||||
} else {
|
||||
await uploadFirmwareToSpecificNode(file, specificNode);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Global firmware upload failed:', error);
|
||||
alert(`Upload failed: ${error.message}`);
|
||||
}
|
||||
|
||||
// Clear file input
|
||||
event.target.value = '';
|
||||
}
|
||||
|
||||
// Upload firmware to all nodes
|
||||
async function uploadFirmwareToAllNodes(file) {
|
||||
const response = await client.getClusterMembers();
|
||||
const nodes = response.members || [];
|
||||
|
||||
if (nodes.length === 0) {
|
||||
alert('No nodes available for firmware update.');
|
||||
return;
|
||||
}
|
||||
|
||||
const confirmed = confirm(`Upload firmware to all ${nodes.length} nodes? This will update: ${nodes.map(n => n.hostname || n.ip).join(', ')}`);
|
||||
if (!confirmed) return;
|
||||
|
||||
// TODO: Implement batch upload logic
|
||||
alert(`Firmware upload to all ${nodes.length} nodes initiated. This feature is coming soon!`);
|
||||
}
|
||||
|
||||
// Upload firmware to specific node
|
||||
async function uploadFirmwareToSpecificNode(file, nodeIp) {
|
||||
const confirmed = confirm(`Upload firmware to node ${nodeIp}?`);
|
||||
if (!confirmed) return;
|
||||
|
||||
// TODO: Implement single node upload logic
|
||||
alert(`Firmware upload to node ${nodeIp} initiated. This feature is coming soon!`);
|
||||
}
|
||||
|
||||
// Populate node select dropdown
|
||||
function populateNodeSelect() {
|
||||
const select = document.getElementById('specific-node-select');
|
||||
if (!select) return;
|
||||
|
||||
// Clear existing options
|
||||
select.innerHTML = '<option value="">Select a node...</option>';
|
||||
|
||||
// Get current cluster members and populate
|
||||
const container = document.getElementById('cluster-members-container');
|
||||
const memberCards = container.querySelectorAll('.member-card');
|
||||
|
||||
memberCards.forEach(card => {
|
||||
const memberIp = card.dataset.memberIp;
|
||||
const hostname = card.querySelector('.member-name')?.textContent || memberIp;
|
||||
|
||||
const option = document.createElement('option');
|
||||
option.value = memberIp;
|
||||
option.textContent = `${hostname} (${memberIp})`;
|
||||
select.appendChild(option);
|
||||
});
|
||||
}
|
||||
|
||||
// Refresh firmware view
|
||||
function refreshFirmwareView() {
|
||||
updateFirmwareStats();
|
||||
populateNodeSelect();
|
||||
}
|
||||
|
||||
// Update firmware statistics
|
||||
function updateFirmwareStats() {
|
||||
const container = document.getElementById('cluster-members-container');
|
||||
const memberCards = container.querySelectorAll('.member-card');
|
||||
|
||||
document.getElementById('total-nodes').textContent = memberCards.length;
|
||||
document.getElementById('available-updates').textContent = '0'; // TODO: Implement update checking
|
||||
document.getElementById('last-update').textContent = 'Never'; // TODO: Implement last update tracking
|
||||
}
|
||||
Reference in New Issue
Block a user