From 91c3db7f4b8c74c1dda55229fa0d09bf9484c512 Mon Sep 17 00:00:00 2001 From: "courtmanr@gmail.com" Date: Tue, 29 Apr 2025 09:57:33 +0100 Subject: [PATCH] feat(ui): Improve PBS tab readability - Parses task targets, shortens UPIDs, updates GC status display. --- src/public/app.js | 177 +++++++++++++++++++++++++++++++--------------- 1 file changed, 119 insertions(+), 58 deletions(-) diff --git a/src/public/app.js b/src/public/app.js index a4ebb719b..bcc556bc8 100644 --- a/src/public/app.js +++ b/src/public/app.js @@ -1720,7 +1720,7 @@ document.addEventListener('DOMContentLoaded', function() { const getPbsGcStatusText = (gcStatus) => { // Handle falsy values, 'unknown', or literal 'N/A' string if (!gcStatus || gcStatus === 'unknown' || gcStatus === 'N/A') { - return 'Unknown'; + return '-'; // Changed from "Unknown" } // Determine color based on known status keywords let colorClass = 'text-gray-600 dark:text-gray-400'; @@ -1768,80 +1768,141 @@ document.addEventListener('DOMContentLoaded', function() { } } - // --- NEW Function to Populate a PBS Task Table --- // MOVED UP & MODIFIED + // ---> ADDED: Helper to parse PBS task target string <--- + const parsePbsTaskTarget = (task) => { + const workerId = task.worker_id || task.id || ''; // e.g., guests:ct/103/681078F1 or hosts:host/bkp-2024-05-15 or guests::ct/103 + const taskType = task.worker_type || task.type || ''; // e.g., backup, verify, prune, garbage_collection + + // Default + let displayTarget = workerId; + + if (taskType === 'backup' || taskType === 'verify') { + // Format: datastore:type/id/snapshot or datastore:host/id + const parts = workerId.split(':'); + if (parts.length >= 2) { + const targetPart = parts[1]; // e.g., ct/103/681078F1 or host/bkp-2024-05-15 + const targetSubParts = targetPart.split('/'); + if (targetSubParts.length >= 2) { + // Try to get guest type and ID (e.g., ct/103 -> type=ct, id=103) + const guestType = targetSubParts[0]; // ct or vm or host + const guestId = targetSubParts[1]; // 103 or bkp-2024-05-15 + displayTarget = `${guestType}/${guestId}`; + } + } + } else if (taskType === 'prune' || taskType === 'garbage_collection') { + // Format: datastore::group or datastore:group + const parts = workerId.split('::'); // Try double colon first for prune groups + if (parts.length === 2) { + displayTarget = `Prune ${parts[0]} (${parts[1]})`; // e.g., Prune guests (ct/103) + } else { + const singleColonParts = workerId.split(':'); // Fallback for GC datastore + if (singleColonParts.length === 1 && workerId !== '') { // GC often just has datastore name + displayTarget = `GC ${workerId}`; + } else if (singleColonParts.length >= 2) { + displayTarget = `Prune ${singleColonParts[0]} (${singleColonParts[1]})` // Fallback prune format? + } + } + } else if (taskType === 'sync') { + // Often just job id? Example: job_id + displayTarget = `Sync Job: ${workerId}`; + } + + // TODO: Add guest name lookup here in the future if possible + // Maybe: find guest name from vmsData/containersData based on parsed type/id + + return displayTarget; // Return the parsed or original string + }; + // ---> END ADDED <--- + + // Function to populate a specific PBS task table function populatePbsTaskTable(parentSectionElement, fullTasksArray) { if (!parentSectionElement) { - console.error("Parent section element not provided to populatePbsTaskTable."); + console.warn('[PBS UI] Parent element not found for task table'); return; } - // Find elements relative to the parent section - const tbody = parentSectionElement.querySelector('.pbs-task-tbody'); - const buttonContainer = parentSectionElement.querySelector('.pbs-toggle-button-container'); + const tableBody = parentSectionElement.querySelector('tbody'); + const showMoreButton = parentSectionElement.querySelector('.pbs-show-more'); + const noTasksMessage = parentSectionElement.querySelector('.pbs-no-tasks'); - if (!tbody || !buttonContainer) { - // If elements aren't found *within the parent*, log an error - console.error(`Required child elements (.pbs-task-tbody or .pbs-toggle-button-container) not found within provided parent section.`, parentSectionElement); - return; + if (!tableBody) { + console.warn('[PBS UI] Table body not found within', parentSectionElement); + return; // Exit if table body doesn't exist } - // Ensure tbody has an ID if it doesn't (needed for button linking) - if (!tbody.id) { - const table = parentSectionElement.querySelector('table'); - tbody.id = table && table.id ? table.id.replace('-table-', '-tbody-') : `pbs-tbody-${Date.now()}-${Math.random()}`; - console.warn(`Assigned dynamic ID to tbody: ${tbody.id}`); - } - - tbody.innerHTML = ''; // Clear previous content - buttonContainer.innerHTML = ''; // Clear previous button + // Clear previous rows + tableBody.innerHTML = ''; const tasks = fullTasksArray || []; // Ensure tasks is an array - const totalTasks = tasks.length; - const limit = INITIAL_PBS_TASK_LIMIT; - const isCurrentlyExpanded = tbody.dataset.isExpanded === 'true'; // Check current state + let displayedTasks = tasks.slice(0, INITIAL_PBS_TASK_LIMIT); - const tasksToDisplay = isCurrentlyExpanded ? tasks : tasks.slice(0, limit); - - // Store full data and state on the tbody - tbody.dataset.fullTasks = JSON.stringify(tasks); // Store all tasks - tbody.dataset.initialLimit = limit; - tbody.dataset.isExpanded = isCurrentlyExpanded ? 'true' : 'false'; - - if (tasksToDisplay.length === 0) { - tbody.innerHTML = `No recent tasks found (last 7 days).`; + if (tasks.length === 0) { + if (noTasksMessage) noTasksMessage.classList.remove('hidden'); + if (showMoreButton) showMoreButton.classList.add('hidden'); } else { - tasksToDisplay.forEach(task => { + if (noTasksMessage) noTasksMessage.classList.add('hidden'); + + displayedTasks.forEach(task => { + const target = parsePbsTaskTarget(task); // Use the new parsing function + const statusIcon = getPbsStatusIcon(task.status); + const startTime = task.startTime ? formatPbsTimestamp(task.startTime) : 'N/A'; + const duration = task.duration !== null ? formatDuration(task.duration) : 'N/A'; + const upid = task.upid || 'N/A'; + // ---> ADDED: Shorten UPID <--- + const shortUpid = upid.length > 30 ? `${upid.substring(0, 15)}...${upid.substring(upid.length - 15)}` : upid; + // ---> END ADDED <--- + const row = document.createElement('tr'); - row.className = 'hover:bg-gray-50 dark:hover:bg-gray-700/50'; + row.className = 'border-b border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors duration-150 ease-in-out'; row.innerHTML = ` - ${task.id || 'N/A'} - ${getPbsStatusIcon(task.status)} - ${formatPbsTimestamp(task.startTime)} - ${formatDuration(task.duration)} - ${task.upid || 'N/A'} - `; - tbody.appendChild(row); + ${target} + ${statusIcon} + ${startTime} + ${duration} + ${shortUpid} + `; // Display shortened UPID, full UPID in title + tableBody.appendChild(row); }); - } - // Add Show More/Less button if needed - if (totalTasks > limit) { - const button = document.createElement('button'); - const isExpanded = tbody.dataset.isExpanded === 'true'; - const buttonText = isExpanded ? 'Show Less' : `Show More (${totalTasks - limit} older)`; - const iconSvg = isExpanded - ? '' - : ''; + if (showMoreButton) { + if (tasks.length > INITIAL_PBS_TASK_LIMIT) { + showMoreButton.classList.remove('hidden'); + const remainingCount = tasks.length - INITIAL_PBS_TASK_LIMIT; + showMoreButton.textContent = `Show More (${remainingCount} older)`; + // Manage click handler carefully to avoid duplicates + if (!showMoreButton.dataset.handlerAttached) { + showMoreButton.addEventListener('click', () => { + // Append the rest of the tasks + tasks.slice(INITIAL_PBS_TASK_LIMIT).forEach(task => { + const target = parsePbsTaskTarget(task); // Use the new parsing function + const statusIcon = getPbsStatusIcon(task.status); + const startTime = task.startTime ? formatPbsTimestamp(task.startTime) : 'N/A'; + const duration = task.duration !== null ? formatDuration(task.duration) : 'N/A'; + const upid = task.upid || 'N/A'; + // ---> ADDED: Shorten UPID <--- + const shortUpid = upid.length > 30 ? `${upid.substring(0, 15)}...${upid.substring(upid.length - 15)}` : upid; + // ---> END ADDED <--- - button.innerHTML = buttonText + iconSvg; - button.type = 'button'; - // Updated classes for button styling - button.className = 'pbs-toggle-button text-xs px-2 py-1 rounded border border-gray-300 dark:border-gray-600 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600 focus:outline-none focus:ring-1 focus:ring-blue-500'; - button.dataset.targetTbodyId = tbody.id; // Use the actual ID of the tbody we found/assigned - buttonContainer.appendChild(button); - } else { - tbody.dataset.isExpanded = 'false'; + const row = document.createElement('tr'); + row.className = 'border-b border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors duration-150 ease-in-out'; + row.innerHTML = ` + ${target} + ${statusIcon} + ${startTime} + ${duration} + ${shortUpid} + `; // Display shortened UPID, full UPID in title + tableBody.appendChild(row); + }); + showMoreButton.classList.add('hidden'); // Hide after showing all + }); + showMoreButton.dataset.handlerAttached = 'true'; // Mark handler as attached + } + } else { + showMoreButton.classList.add('hidden'); + } + } } - } +} // --- END NEW Function --- // --- Updated Function: Update PBS Info Section (Upsert Logic) ---