fix: upload progress bars

This commit is contained in:
2025-08-26 12:50:44 +02:00
parent d712206377
commit e0dedc1c23
2 changed files with 183 additions and 19 deletions

View File

@@ -791,8 +791,8 @@ async function uploadFirmwareToSpecificNode(file, nodeIp) {
// Show upload progress area // Show upload progress area
showFirmwareUploadProgress(file, [{ ip: nodeIp, hostname: nodeIp }]); showFirmwareUploadProgress(file, [{ ip: nodeIp, hostname: nodeIp }]);
// Perform single node upload // Perform single node upload with progress tracking
const result = await performSingleFirmwareUpload(file, nodeIp); const result = await performSingleFirmwareUploadWithProgress(file, nodeIp);
// Display results // Display results
displayFirmwareUploadResults([result]); displayFirmwareUploadResults([result]);
@@ -807,21 +807,24 @@ async function uploadFirmwareToSpecificNode(file, nodeIp) {
async function performBatchFirmwareUpload(file, nodes) { async function performBatchFirmwareUpload(file, nodes) {
const results = []; const results = [];
const totalNodes = nodes.length; const totalNodes = nodes.length;
let successfulUploads = 0;
for (let i = 0; i < nodes.length; i++) { for (let i = 0; i < nodes.length; i++) {
const node = nodes[i]; const node = nodes[i];
const nodeIp = node.ip; const nodeIp = node.ip;
try { try {
// Update progress // Update progress - show current node being processed
updateFirmwareUploadProgress(i + 1, totalNodes, nodeIp, 'Uploading...'); updateFirmwareUploadProgress(i + 1, totalNodes, nodeIp, 'Uploading...');
// Upload to this node // Upload to this node
const result = await performSingleFirmwareUpload(file, nodeIp); const result = await performSingleFirmwareUpload(file, nodeIp);
results.push(result); results.push(result);
successfulUploads++;
// Update progress // Update progress - show completion and update progress bar with actual success rate
updateFirmwareUploadProgress(i + 1, totalNodes, nodeIp, 'Completed'); updateFirmwareUploadProgress(i + 1, totalNodes, nodeIp, 'Completed');
updateMultiNodeProgress(successfulUploads, totalNodes);
} catch (error) { } catch (error) {
console.error(`Failed to upload to node ${nodeIp}:`, error); console.error(`Failed to upload to node ${nodeIp}:`, error);
@@ -834,8 +837,9 @@ async function performBatchFirmwareUpload(file, nodes) {
}; };
results.push(errorResult); results.push(errorResult);
// Update progress // Update progress - show failure and update progress bar with actual success rate
updateFirmwareUploadProgress(i + 1, totalNodes, nodeIp, 'Failed'); updateFirmwareUploadProgress(i + 1, totalNodes, nodeIp, 'Failed');
updateMultiNodeProgress(successfulUploads, totalNodes);
} }
// Small delay between uploads to avoid overwhelming the network // Small delay between uploads to avoid overwhelming the network
@@ -844,6 +848,9 @@ async function performBatchFirmwareUpload(file, nodes) {
} }
} }
// Update final progress based on successful uploads
updateFinalProgress(successfulUploads, totalNodes);
return results; return results;
} }
@@ -880,6 +887,102 @@ async function performSingleFirmwareUpload(file, nodeIp) {
} }
} }
// Perform single firmware upload to a specific node with progress tracking
async function performSingleFirmwareUploadWithProgress(file, nodeIp) {
try {
// Simulate upload progress for single node
await simulateUploadProgress(nodeIp);
// Create FormData for the upload
const formData = new FormData();
formData.append('file', file);
// Upload to backend
const response = await fetch(`/api/node/update?ip=${encodeURIComponent(nodeIp)}`, {
method: 'POST',
body: formData
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.message || `HTTP ${response.status}: ${response.statusText}`);
}
const result = await response.json();
return {
nodeIp: nodeIp,
hostname: nodeIp, // Will be updated if we have more info
success: true,
result: result,
timestamp: new Date().toISOString()
};
} catch (error) {
throw new Error(`Upload to ${nodeIp} failed: ${error.message}`);
}
}
// Simulate upload progress for single node uploads
async function simulateUploadProgress(nodeIp) {
const progressSteps = [10, 25, 50, 75, 90, 100];
const totalSteps = progressSteps.length;
for (let i = 0; i < totalSteps; i++) {
const progress = progressSteps[i];
updateSingleNodeProgress(progress, nodeIp);
// Wait a bit between progress updates (simulating actual upload time)
if (i < totalSteps - 1) {
await new Promise(resolve => setTimeout(resolve, 300));
}
}
}
// Update progress for single node uploads
function updateSingleNodeProgress(percentage, nodeIp) {
const progressBar = document.getElementById('overall-progress-bar');
const progressText = document.querySelector('.progress-text');
if (progressBar && progressText) {
progressBar.style.width = `${percentage}%`;
progressText.textContent = `${percentage}% Complete`;
// Update progress bar color based on completion
if (percentage === 100) {
progressBar.style.backgroundColor = '#4ade80';
} else if (percentage > 50) {
progressBar.style.backgroundColor = '#60a5fa';
} else {
progressBar.style.backgroundColor = '#fbbf24';
}
}
}
// Update final progress based on successful vs total uploads
function updateFinalProgress(successfulUploads, totalNodes) {
const progressBar = document.getElementById('overall-progress-bar');
const progressText = document.querySelector('.progress-text');
const progressHeader = document.querySelector('.progress-header h3');
if (progressBar && progressText) {
const successPercentage = Math.round((successfulUploads / totalNodes) * 100);
progressBar.style.width = `${successPercentage}%`;
if (successfulUploads === totalNodes) {
progressText.textContent = '100% Complete';
progressBar.style.backgroundColor = '#4ade80';
} else {
progressText.textContent = `${successfulUploads}/${totalNodes} Successful`;
progressBar.style.backgroundColor = '#f87171';
}
}
if (progressHeader) {
progressHeader.textContent = `📤 Firmware Upload Results (${successfulUploads}/${totalNodes} Successful)`;
}
}
// Show firmware upload progress area // Show firmware upload progress area
function showFirmwareUploadProgress(file, nodes) { function showFirmwareUploadProgress(file, nodes) {
const container = document.getElementById('firmware-nodes-list'); const container = document.getElementById('firmware-nodes-list');
@@ -895,9 +998,12 @@ function showFirmwareUploadProgress(file, nodes) {
</div> </div>
<div class="overall-progress"> <div class="overall-progress">
<div class="progress-bar-container"> <div class="progress-bar-container">
<div class="progress-bar" id="overall-progress-bar"></div> <div class="progress-bar" id="overall-progress-bar" style="width: 0%; background-color: #fbbf24;"></div>
</div> </div>
<span class="progress-text">0% Complete</span> <span class="progress-text">0/0 Successful (0%)</span>
</div>
<div class="progress-summary" id="progress-summary">
<span>Status: Preparing upload...</span>
</div> </div>
</div> </div>
<div class="progress-list" id="progress-list"> <div class="progress-list" id="progress-list">
@@ -949,24 +1055,58 @@ function updateFirmwareUploadProgress(current, total, nodeIp, status) {
} }
} }
// Update overall progress // Update progress header to show current node being processed
const progressHeader = document.querySelector('.progress-header h3'); const progressHeader = document.querySelector('.progress-header h3');
const progressBar = document.getElementById('overall-progress-bar');
const progressText = document.querySelector('.progress-text');
if (progressHeader) { if (progressHeader) {
progressHeader.textContent = `📤 Firmware Upload Progress (${current}/${total})`; progressHeader.textContent = `📤 Firmware Upload Progress (${current}/${total})`;
} }
// Update progress summary
const progressSummary = document.getElementById('progress-summary');
if (progressSummary) {
if (status === 'Uploading...') {
progressSummary.innerHTML = `<span>Status: Uploading to ${nodeIp} (${current}/${total})</span>`;
} else if (status === 'Completed') {
// For multi-node uploads, show success rate
if (total > 1) {
const successfulNodes = document.querySelectorAll('.progress-status.success').length;
const totalNodes = total;
const successRate = Math.round((successfulNodes / totalNodes) * 100);
progressSummary.innerHTML = `<span>Status: Completed upload to ${nodeIp}. Overall: ${successfulNodes}/${totalNodes} successful (${successRate}%)</span>`;
} else {
progressSummary.innerHTML = `<span>Status: Completed upload to ${nodeIp} (${current}/${total})</span>`;
}
} else if (status === 'Failed') {
// For multi-node uploads, show success rate
if (total > 1) {
const successfulNodes = document.querySelectorAll('.progress-status.success').length;
const totalNodes = total;
const successRate = Math.round((successfulNodes / totalNodes) * 100);
progressSummary.innerHTML = `<span>Status: Failed upload to ${nodeIp}. Overall: ${successfulNodes}/${totalNodes} successful (${successRate}%)</span>`;
} else {
progressSummary.innerHTML = `<span>Status: Failed upload to ${nodeIp} (${current}/${total})</span>`;
}
}
}
// IMPORTANT: Do NOT update the progress bar here - let updateMultiNodeProgress handle it
// The progress bar should only reflect actual successful uploads, not nodes processed
}
// Update progress for multi-node uploads based on actual success rate
function updateMultiNodeProgress(successfulUploads, totalNodes) {
const progressBar = document.getElementById('overall-progress-bar');
const progressText = document.querySelector('.progress-text');
if (progressBar && progressText) { if (progressBar && progressText) {
const percentage = Math.round((current / total) * 100); const successPercentage = Math.round((successfulUploads / totalNodes) * 100);
progressBar.style.width = `${percentage}%`; progressBar.style.width = `${successPercentage}%`;
progressText.textContent = `${percentage}% Complete`; progressText.textContent = `${successfulUploads}/${totalNodes} Successful (${successPercentage}%)`;
// Update progress bar color based on completion // Update progress bar color based on completion
if (percentage === 100) { if (successPercentage === 100) {
progressBar.style.backgroundColor = '#4ade80'; progressBar.style.backgroundColor = '#4ade80';
} else if (percentage > 50) { } else if (successPercentage > 50) {
progressBar.style.backgroundColor = '#60a5fa'; progressBar.style.backgroundColor = '#60a5fa';
} else { } else {
progressBar.style.backgroundColor = '#fbbf24'; progressBar.style.backgroundColor = '#fbbf24';
@@ -1017,13 +1157,23 @@ function displayFirmwareUploadResults(results) {
</div> </div>
`; `;
container.innerHTML = resultsHTML; // Append results below the progress area instead of replacing everything
// First, check if there's already a results section and remove it
const existingResults = container.querySelector('#firmware-upload-results');
if (existingResults) {
existingResults.remove();
}
// Append the new results below the progress area
container.insertAdjacentHTML('beforeend', resultsHTML);
} }
// Clear firmware upload results // Clear firmware upload results
function clearFirmwareResults() { function clearFirmwareResults() {
const container = document.getElementById('firmware-nodes-list'); const resultsSection = document.getElementById('firmware-upload-results');
container.innerHTML = ''; if (resultsSection) {
resultsSection.remove();
}
} }
// Populate node select dropdown // Populate node select dropdown

View File

@@ -1180,6 +1180,20 @@ p {
text-align: right; text-align: right;
} }
.progress-summary {
margin-top: 0.75rem;
padding: 0.5rem 0.75rem;
background: rgba(255, 255, 255, 0.05);
border-radius: 8px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.progress-summary span {
font-size: 0.9rem;
color: rgba(255, 255, 255, 0.8);
font-weight: 500;
}
.success-count { .success-count {
color: #4ade80 !important; color: #4ade80 !important;
border-color: rgba(74, 222, 128, 0.3) !important; border-color: rgba(74, 222, 128, 0.3) !important;