351 lines
14 KiB
HTML
351 lines
14 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Deploy Button Test</title>
|
|
<style>
|
|
body {
|
|
font-family: Arial, sans-serif;
|
|
max-width: 800px;
|
|
margin: 0 auto;
|
|
padding: 20px;
|
|
background: #1a202c;
|
|
color: white;
|
|
}
|
|
.test-section {
|
|
background: rgba(255, 255, 255, 0.05);
|
|
padding: 20px;
|
|
margin: 20px 0;
|
|
border-radius: 8px;
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
}
|
|
.firmware-actions {
|
|
background: rgba(0, 0, 0, 0.3);
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
}
|
|
.target-options {
|
|
display: flex;
|
|
gap: 20px;
|
|
margin-bottom: 20px;
|
|
}
|
|
.target-option {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
cursor: pointer;
|
|
}
|
|
.file-input-wrapper {
|
|
margin: 20px 0;
|
|
}
|
|
.deploy-btn {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
border: none;
|
|
color: white;
|
|
padding: 10px 20px;
|
|
border-radius: 6px;
|
|
cursor: pointer;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
}
|
|
.deploy-btn:disabled {
|
|
background: rgba(255, 255, 255, 0.1);
|
|
color: rgba(255, 255, 255, 0.4);
|
|
cursor: not-allowed;
|
|
}
|
|
.node-select {
|
|
background: #2d3748;
|
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
|
color: white;
|
|
padding: 5px 10px;
|
|
border-radius: 4px;
|
|
margin-left: 10px;
|
|
}
|
|
.no-nodes-message {
|
|
color: #fbbf24;
|
|
font-size: 0.8rem;
|
|
margin-top: 0.25rem;
|
|
font-style: italic;
|
|
text-align: center;
|
|
padding: 0.25rem;
|
|
border-radius: 4px;
|
|
background: rgba(251, 191, 36, 0.1);
|
|
border: 1px solid rgba(251, 191, 36, 0.3);
|
|
}
|
|
.cluster-members {
|
|
background: rgba(0, 0, 0, 0.2);
|
|
padding: 15px;
|
|
border-radius: 6px;
|
|
margin: 20px 0;
|
|
}
|
|
.member-card {
|
|
background: rgba(255, 255, 255, 0.05);
|
|
padding: 10px;
|
|
margin: 10px 0;
|
|
border-radius: 4px;
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
}
|
|
.status-online {
|
|
color: #4ade80;
|
|
}
|
|
.status-offline {
|
|
color: #f87171;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>🚀 Deploy Button Test</h1>
|
|
|
|
<div class="test-section">
|
|
<h2>Test Scenario: Deploy Button State</h2>
|
|
<p>This test demonstrates the deploy button behavior when:</p>
|
|
<ul>
|
|
<li>No file is selected</li>
|
|
<li>No nodes are available</li>
|
|
<li>File is selected but no target is chosen</li>
|
|
<li>File is selected and target is chosen</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="firmware-actions">
|
|
<h3>🚀 Firmware Update</h3>
|
|
|
|
<div class="target-options">
|
|
<label class="target-option">
|
|
<input type="radio" name="target-type" value="all" checked>
|
|
<span>All Nodes</span>
|
|
</label>
|
|
<label class="target-option">
|
|
<input type="radio" name="target-type" value="specific">
|
|
<span>Specific Node</span>
|
|
<select id="specific-node-select" class="node-select" style="visibility: hidden; opacity: 0;">
|
|
<option value="">Select a node...</option>
|
|
</select>
|
|
</label>
|
|
</div>
|
|
|
|
<div class="file-input-wrapper">
|
|
<input type="file" id="global-firmware-file" accept=".bin,.hex" style="display: none;">
|
|
<button onclick="document.getElementById('global-firmware-file').click()">
|
|
📁 Choose File
|
|
</button>
|
|
<span id="file-info">No file selected</span>
|
|
</div>
|
|
|
|
<button class="deploy-btn" id="deploy-btn" disabled>
|
|
🚀 Deploy Firmware
|
|
</button>
|
|
</div>
|
|
|
|
<div class="cluster-members">
|
|
<h3>Cluster Members</h3>
|
|
<div id="cluster-members-container">
|
|
<div class="loading">Loading cluster members...</div>
|
|
</div>
|
|
<button onclick="addTestNode()">Add Test Node</button>
|
|
<button onclick="removeAllNodes()">Remove All Nodes</button>
|
|
</div>
|
|
|
|
<div class="test-section">
|
|
<h2>Test Instructions</h2>
|
|
<ol>
|
|
<li>Select "Specific Node" radio button - notice the deploy button remains disabled</li>
|
|
<li>Click "Add Test Node" to simulate cluster discovery</li>
|
|
<li>Select "Specific Node" again - now you should see nodes in the dropdown</li>
|
|
<li>Select a file - deploy button should remain disabled until you select a node</li>
|
|
<li>Select a specific node - deploy button should now be enabled</li>
|
|
<li>Click "Remove All Nodes" to test the "no nodes available" state</li>
|
|
</ol>
|
|
</div>
|
|
|
|
<script>
|
|
// Simulate the cluster members functionality
|
|
let testNodes = [];
|
|
|
|
function addTestNode() {
|
|
const nodeCount = testNodes.length + 1;
|
|
const newNode = {
|
|
ip: `192.168.1.${100 + nodeCount}`,
|
|
hostname: `TestNode${nodeCount}`,
|
|
status: 'active',
|
|
latency: Math.floor(Math.random() * 50) + 10
|
|
};
|
|
testNodes.push(newNode);
|
|
displayClusterMembers();
|
|
populateNodeSelect();
|
|
updateDeployButton();
|
|
}
|
|
|
|
function removeAllNodes() {
|
|
testNodes = [];
|
|
displayClusterMembers();
|
|
populateNodeSelect();
|
|
updateDeployButton();
|
|
}
|
|
|
|
function displayClusterMembers() {
|
|
const container = document.getElementById('cluster-members-container');
|
|
|
|
if (testNodes.length === 0) {
|
|
container.innerHTML = '<div class="loading">No cluster members found</div>';
|
|
return;
|
|
}
|
|
|
|
const membersHTML = testNodes.map(node => {
|
|
const statusClass = node.status === 'active' ? 'status-online' : 'status-offline';
|
|
const statusText = node.status === 'active' ? 'Online' : 'Offline';
|
|
const statusIcon = node.status === 'active' ? '🟢' : '🔴';
|
|
|
|
return `
|
|
<div class="member-card" data-member-ip="${node.ip}">
|
|
<div class="member-name">${node.hostname}</div>
|
|
<div class="member-ip">${node.ip}</div>
|
|
<div class="member-status ${statusClass}">
|
|
${statusIcon} ${statusText}
|
|
</div>
|
|
<div class="member-latency">Latency: ${node.latency}ms</div>
|
|
</div>
|
|
`;
|
|
}).join('');
|
|
|
|
container.innerHTML = membersHTML;
|
|
}
|
|
|
|
function populateNodeSelect() {
|
|
const select = document.getElementById('specific-node-select');
|
|
if (!select) return;
|
|
|
|
select.innerHTML = '<option value="">Select a node...</option>';
|
|
|
|
if (testNodes.length === 0) {
|
|
const option = document.createElement('option');
|
|
option.value = "";
|
|
option.textContent = "No nodes available";
|
|
option.disabled = true;
|
|
select.appendChild(option);
|
|
return;
|
|
}
|
|
|
|
testNodes.forEach(node => {
|
|
const option = document.createElement('option');
|
|
option.value = node.ip;
|
|
option.textContent = `${node.hostname} (${node.ip})`;
|
|
select.appendChild(option);
|
|
});
|
|
}
|
|
|
|
function updateDeployButton() {
|
|
const deployBtn = document.getElementById('deploy-btn');
|
|
const fileInput = document.getElementById('global-firmware-file');
|
|
const targetType = document.querySelector('input[name="target-type"]:checked');
|
|
const specificNodeSelect = document.getElementById('specific-node-select');
|
|
|
|
if (!deployBtn || !fileInput) return;
|
|
|
|
const hasFile = fileInput.files && fileInput.files.length > 0;
|
|
const hasAvailableNodes = testNodes.length > 0;
|
|
|
|
let isValidTarget = false;
|
|
if (targetType.value === 'all') {
|
|
isValidTarget = hasAvailableNodes;
|
|
} else if (targetType.value === 'specific') {
|
|
isValidTarget = hasAvailableNodes && specificNodeSelect.value && specificNodeSelect.value !== "";
|
|
}
|
|
|
|
deployBtn.disabled = !hasFile || !isValidTarget;
|
|
|
|
// Update button text to provide better feedback
|
|
if (!hasAvailableNodes) {
|
|
deployBtn.textContent = '🚀 Deploy (No nodes available)';
|
|
deployBtn.title = 'No cluster nodes are currently available for deployment';
|
|
} else if (!hasFile) {
|
|
deployBtn.textContent = '🚀 Deploy Firmware';
|
|
deployBtn.title = 'Please select a firmware file to deploy';
|
|
} else if (!isValidTarget) {
|
|
deployBtn.textContent = '🚀 Deploy Firmware';
|
|
deployBtn.title = 'Please select a valid target for deployment';
|
|
} else {
|
|
deployBtn.textContent = '🚀 Deploy Firmware';
|
|
deployBtn.title = 'Ready to deploy firmware';
|
|
}
|
|
}
|
|
|
|
// Setup event listeners
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// 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.visibility = 'visible';
|
|
specificNodeSelect.style.opacity = '1';
|
|
populateNodeSelect();
|
|
|
|
// Check if there are any nodes available and show appropriate message
|
|
if (testNodes.length === 0) {
|
|
// Show a message that no nodes are available
|
|
const noNodesMsg = document.createElement('div');
|
|
noNodesMsg.className = 'no-nodes-message';
|
|
noNodesMsg.textContent = 'No cluster nodes are currently available';
|
|
|
|
// Remove any existing message
|
|
const existingMsg = specificNodeSelect.parentNode.querySelector('.no-nodes-message');
|
|
if (existingMsg) {
|
|
existingMsg.remove();
|
|
}
|
|
|
|
specificNodeSelect.parentNode.appendChild(noNodesMsg);
|
|
} else {
|
|
// Remove any existing no-nodes message
|
|
const existingMsg = specificNodeSelect.parentNode.querySelector('.no-nodes-message');
|
|
if (existingMsg) {
|
|
existingMsg.remove();
|
|
}
|
|
}
|
|
} else {
|
|
specificNodeSelect.style.visibility = 'hidden';
|
|
specificNodeSelect.style.opacity = '0';
|
|
|
|
// Remove any no-nodes message when hiding
|
|
const existingMsg = specificNodeSelect.parentNode.querySelector('.no-nodes-message');
|
|
if (existingMsg) {
|
|
existingMsg.remove();
|
|
}
|
|
}
|
|
updateDeployButton();
|
|
});
|
|
});
|
|
|
|
// Setup specific node select change handler
|
|
if (specificNodeSelect) {
|
|
specificNodeSelect.addEventListener('change', updateDeployButton);
|
|
}
|
|
|
|
// Setup file input change handler
|
|
const fileInput = document.getElementById('global-firmware-file');
|
|
if (fileInput) {
|
|
fileInput.addEventListener('change', (event) => {
|
|
const file = event.target.files[0];
|
|
const fileInfo = document.getElementById('file-info');
|
|
|
|
if (file) {
|
|
fileInfo.textContent = `${file.name} (${(file.size / 1024).toFixed(1)}KB)`;
|
|
} else {
|
|
fileInfo.textContent = 'No file selected';
|
|
}
|
|
|
|
updateDeployButton();
|
|
});
|
|
}
|
|
|
|
// Initial setup
|
|
displayClusterMembers();
|
|
populateNodeSelect();
|
|
updateDeployButton();
|
|
});
|
|
</script>
|
|
</body>
|
|
</html> |