diff --git a/public/app.js b/public/app.js index 93ec46ff7..dbe88d2e9 100644 --- a/public/app.js +++ b/public/app.js @@ -130,9 +130,46 @@ document.addEventListener('DOMContentLoaded', function() { let groupByNode = true; // Default view let filterGuestType = 'all'; // Default filter const AVERAGING_WINDOW_SIZE = 5; - const dashboardHistory = {}; + const dashboardHistory = {}; // Re-add this line let filterStatus = 'all'; // New state variable for status filter let initialDataReceived = false; // Flag to control initial rendering + let storageData = {}; // Add state for storage data + + // --- Global Helper for Text Progress Bar --- + const createProgressTextBarHTML = (percent, text, colorClass) => { + const numericPercent = isNaN(parseInt(percent)) ? 0 : parseInt(percent); + const textColorClass = 'text-gray-700 dark:text-gray-200'; + + return ` + +
Error: ${storage.globalError}
`; + return; + } + + if (!storage || Object.keys(storage).length === 0) { + // Empty message styling - remove card styles, just use text/padding + contentDiv.innerHTML = 'No storage data available or failed to load for any node.
'; + return; + } + + // --- Helper function for Storage Icons --- + function getStorageTypeIcon(type) { + // Simple icons using Tailwind/SVG - can be expanded + switch(type) { + case 'dir': + return ''; // Folder + case 'lvm': + case 'lvmthin': + return ''; // Database (representing logical volume) + case 'zfs': + case 'zfspool': + return ''; // Activity (representing ZFS complexity/features) + case 'nfs': + case 'cifs': + return ''; // Share-2 + case 'cephfs': + case 'rbd': + return ''; // Server (representing distributed storage) + default: + return ''; // HelpCircle (unknown) + } + } + // --- End Helper --- + + // --- Updated Helper for Content Badge Details (Class + Tooltip) --- + function getContentBadgeDetails(contentType) { + let details = { + badgeClass: 'bg-gray-200 dark:bg-gray-600 text-gray-700 dark:text-gray-300', // Default style + tooltip: `Content type: ${contentType}` // Default tooltip + }; + + switch(contentType) { + case 'iso': + details.badgeClass = 'bg-blue-100 dark:bg-blue-900/50 text-blue-700 dark:text-blue-300'; + details.tooltip = 'ISO images (e.g., for OS installation)'; + break; + case 'vztmpl': + details.badgeClass = 'bg-purple-100 dark:bg-purple-900/50 text-purple-700 dark:text-purple-300'; + details.tooltip = 'Container templates'; + break; + case 'backup': + details.badgeClass = 'bg-orange-100 dark:bg-orange-900/50 text-orange-700 dark:text-orange-300'; + details.tooltip = 'VM/Container backup files (vzdump)'; + break; + case 'images': + details.badgeClass = 'bg-teal-100 dark:bg-teal-900/50 text-teal-700 dark:text-teal-300'; + details.tooltip = 'VM disk images (qcow2, raw, etc.)'; + break; + case 'rootdir': + details.badgeClass = 'bg-red-100 dark:bg-red-900/50 text-red-700 dark:text-red-300'; + details.tooltip = 'Storage for container root filesystems'; + break; + case 'snippets': + details.badgeClass = 'bg-pink-100 dark:bg-pink-900/50 text-pink-700 dark:text-pink-300'; + details.tooltip = 'Snippet files (e.g., cloud-init configs)'; + break; + // Add more cases as needed + } + return details; + } + // --- End Helper --- + + // --- Helper: Sort storage array --- + function sortNodeStorageData(storageArray) { + if (!storageArray || !Array.isArray(storageArray)) return []; + // Create a shallow copy to avoid modifying the original + const sortedArray = [...storageArray]; + sortedArray.sort((a, b) => { + const nameA = String(a.storage || '').toLowerCase(); + const nameB = String(b.storage || '').toLowerCase(); + return nameA.localeCompare(nameB); + }); + return sortedArray; + } + // --- End Helper --- + + // Create ONE table for all nodes + const table = document.createElement('table'); + table.className = 'w-full text-sm border-collapse table-fixed'; + + const thead = document.createElement('thead'); + // Define widths for most columns, leave Usage column without width + thead.innerHTML = ` +