feat: improve styling, add more infos to node cards

This commit is contained in:
2025-09-20 13:28:10 +02:00
parent e4cfb77a67
commit 22adf7d65f
5 changed files with 165 additions and 7 deletions

View File

@@ -132,6 +132,46 @@ class MonitoringViewComponent extends Component {
return 'utilization-red';
}
// Format lastSeen timestamp to human readable format
formatLastSeen(lastSeen) {
if (!lastSeen) return 'Unknown';
// lastSeen appears to be in milliseconds
const now = Date.now();
const diff = now - lastSeen;
if (diff < 60000) { // Less than 1 minute
return 'Just now';
} else if (diff < 3600000) { // Less than 1 hour
const minutes = Math.floor(diff / 60000);
return `${minutes}m ago`;
} else if (diff < 86400000) { // Less than 1 day
const hours = Math.floor(diff / 3600000);
const minutes = Math.floor((diff % 3600000) / 60000);
return `${hours}h ${minutes}m ago`;
} else { // More than 1 day
const days = Math.floor(diff / 86400000);
const hours = Math.floor((diff % 86400000) / 3600000);
return `${days}d ${hours}h ago`;
}
}
// Format flash size in human readable format
formatFlashSize(bytes) {
if (!bytes || bytes === 0) return 'Unknown';
const units = ['B', 'KB', 'MB', 'GB'];
let size = bytes;
let unitIndex = 0;
while (size >= 1024 && unitIndex < units.length - 1) {
size /= 1024;
unitIndex++;
}
return `${size.toFixed(1)} ${units[unitIndex]}`;
}
// Clean up resources
cleanup() {
if (this.drawer) {
@@ -323,6 +363,7 @@ class MonitoringViewComponent extends Component {
renderNodeCard(nodeData) {
const { ip, hostname, resources, hasResources, error, resourceSource } = nodeData;
if (!hasResources) {
return `
<div class="node-card error" data-node-ip="${ip}">
@@ -336,6 +377,12 @@ class MonitoringViewComponent extends Component {
<div class="node-error">
${error || 'Monitoring endpoint not available'}
</div>
${nodeData.lastSeen ? `
<div class="node-uptime">
<div class="uptime-label">⏱️ Last Seen</div>
<div class="uptime-value">${this.formatLastSeen(nodeData.lastSeen)}</div>
</div>
` : ''}
</div>
`;
}
@@ -344,6 +391,7 @@ class MonitoringViewComponent extends Component {
const cpu = resources?.cpu || {};
const memory = resources?.memory || {};
const storage = resources?.filesystem || resources?.storage || {};
const system = resources?.system || {};
let cpuTotal, cpuAvailable, cpuUsed, cpuUtilization;
let memoryTotal, memoryAvailable, memoryUsed, memoryUtilization;
@@ -398,6 +446,26 @@ class MonitoringViewComponent extends Component {
<span class="status-badge ${resourceSource === 'monitoring' ? 'success' : 'warning'}">${resourceSourceText}</span>
</div>
${system.uptime_formatted ? `
<div class="node-uptime">
<div class="uptime-label">⏱️ Uptime</div>
<div class="uptime-value">${system.uptime_formatted}</div>
</div>
` : ''}
<div class="node-latency">
<div class="latency-label">🐢 Latency</div>
<div class="latency-value">${nodeData.latency ? `${nodeData.latency}ms` : 'N/A'}</div>
</div>
<div class="latency-divider"></div>
${(nodeData.basic?.flashChipSize || nodeData.resources?.flashChipSize) ? `
<div class="node-flash">
<div class="flash-label">💾 Flash</div>
<div class="flash-value">${this.formatFlashSize(nodeData.basic?.flashChipSize || nodeData.resources?.flashChipSize)}</div>
</div>
` : ''}
<div class="node-resources">
<div class="resource-item">
<div class="resource-label">⚡ CPU</div>

View File

@@ -25,6 +25,22 @@ class NodeDetailsComponent extends Component {
return (r << 16) + (g << 8) + b;
}
// Format flash size in human readable format
formatFlashSize(bytes) {
if (!bytes || bytes === 0) return 'Unknown';
const units = ['B', 'KB', 'MB', 'GB'];
let size = bytes;
let unitIndex = 0;
while (size >= 1024 && unitIndex < units.length - 1) {
size /= 1024;
unitIndex++;
}
return `${size.toFixed(1)} ${units[unitIndex]}`;
}
// Parameter component renderers
renderSelectComponent(p, formId, pidx) {
return `<select id="${formId}-field-${pidx}" data-param-name="${p.name}" data-param-location="${p.location || 'body'}" data-param-type="${p.type || 'string'}" data-param-required="${p.required ? '1' : '0'}" class="param-input">${p.values.map(v => `<option value="${v}">${v}</option>`).join('')}</select>`;
@@ -333,7 +349,7 @@ class NodeDetailsComponent extends Component {
</div>
<div class="detail-row">
<span class="detail-label">Flash Size:</span>
<span class="detail-value">${Math.round(nodeStatus.flashChipSize / 1024)}KB</span>
<span class="detail-value">${this.formatFlashSize(nodeStatus.flashChipSize)}</span>
</div>
`;

View File

@@ -725,6 +725,7 @@ class MonitoringViewModel extends ViewModel {
nodeResources.set(member.ip, {
...member,
resources: basicResources,
basic: member.resources, // Preserve original basic data
hasResources: !!basicResources,
resourceSource: basicResources ? 'basic' : 'none',
error: basicResources ? null : error.message