diff --git a/src/public/js/ui/pbs.js b/src/public/js/ui/pbs.js
index c32cdb9be..b7fd2eb04 100644
--- a/src/public/js/ui/pbs.js
+++ b/src/public/js/ui/pbs.js
@@ -172,7 +172,7 @@ PulseApp.ui.pbs = (() => {
const _createTaskTableRow = (task) => {
const target = parsePbsTaskTarget(task);
- const statusIcon = getPbsStatusIcon(task.status);
+ const statusIconHTML = getPbsStatusIcon(task.status);
const startTime = task.startTime ? PulseApp.utils.formatPbsTimestamp(task.startTime) : 'N/A';
const duration = task.duration !== null ? PulseApp.utils.formatDuration(task.duration) : 'N/A';
const upid = task.upid || 'N/A';
@@ -180,13 +180,33 @@ PulseApp.ui.pbs = (() => {
const row = document.createElement('tr');
row.className = `${CSS_CLASSES.BORDER_B_GRAY_200_DARK_GRAY_700} ${CSS_CLASSES.HOVER_BG_GRAY_50_DARK_HOVER_BG_GRAY_700_50} ${CSS_CLASSES.TRANSITION_COLORS} ${CSS_CLASSES.DURATION_150} ${CSS_CLASSES.EASE_IN_OUT}`;
- row.innerHTML = `
-
${target} |
- ${statusIcon} |
- ${startTime} |
- ${duration} |
- ${shortUpid} |
- `;
+
+ const targetCell = document.createElement('td');
+ targetCell.className = `${CSS_CLASSES.P1_PX2} ${CSS_CLASSES.TEXT_SM} ${CSS_CLASSES.TEXT_GRAY_700_DARK_GRAY_300}`;
+ targetCell.textContent = target;
+ row.appendChild(targetCell);
+
+ const statusCell = document.createElement('td');
+ statusCell.className = `${CSS_CLASSES.P1_PX2} ${CSS_CLASSES.TEXT_SM}`;
+ statusCell.innerHTML = statusIconHTML;
+ row.appendChild(statusCell);
+
+ const startTimeCell = document.createElement('td');
+ startTimeCell.className = `${CSS_CLASSES.P1_PX2} ${CSS_CLASSES.TEXT_SM} ${CSS_CLASSES.TEXT_GRAY_500_DARK_GRAY_400} ${CSS_CLASSES.WHITESPACE_NOWRAP}`;
+ startTimeCell.textContent = startTime;
+ row.appendChild(startTimeCell);
+
+ const durationCell = document.createElement('td');
+ durationCell.className = `${CSS_CLASSES.P1_PX2} ${CSS_CLASSES.TEXT_SM} ${CSS_CLASSES.TEXT_GRAY_500_DARK_GRAY_400} ${CSS_CLASSES.WHITESPACE_NOWRAP}`;
+ durationCell.textContent = duration;
+ row.appendChild(durationCell);
+
+ const upidCell = document.createElement('td');
+ upidCell.className = `${CSS_CLASSES.P1_PX2} ${CSS_CLASSES.TEXT_XS} ${CSS_CLASSES.FONT_MONO} ${CSS_CLASSES.TEXT_GRAY_400} dark:text-gray-500 ${CSS_CLASSES.TRUNCATE}`;
+ upidCell.title = upid;
+ upidCell.textContent = shortUpid;
+ row.appendChild(upidCell);
+
return row;
};
@@ -247,7 +267,11 @@ PulseApp.ui.pbs = (() => {
if (showDetails && datastores) {
if (datastores.length === 0) {
- dsTableBody.innerHTML = `| No PBS datastores found or accessible. |
`;
+ const row = dsTableBody.insertRow();
+ const cell = row.insertCell();
+ cell.colSpan = 7;
+ cell.className = `px-4 py-4 text-sm text-gray-400 text-center`;
+ cell.textContent = 'No PBS datastores found or accessible.';
} else {
datastores.forEach(ds => {
const totalBytes = ds.total || 0;
@@ -257,14 +281,39 @@ PulseApp.ui.pbs = (() => {
const usageColor = PulseApp.utils.getUsageColor(usagePercent);
const usageText = totalBytes > 0 ? `${usagePercent}% (${PulseApp.utils.formatBytes(usedBytes)} of ${PulseApp.utils.formatBytes(totalBytes)})` : 'N/A';
const gcStatusHtml = getPbsGcStatusText(ds.gcStatus);
- const row = document.createElement('tr');
+
+ const row = dsTableBody.insertRow();
row.className = `${CSS_CLASSES.HOVER_BG_GRAY_50_DARK_HOVER_BG_GRAY_700_50}`;
- row.innerHTML = `${ds.name || 'N/A'} | ${ds.path || 'N/A'} | ${PulseApp.utils.formatBytes(usedBytes)} | ${PulseApp.utils.formatBytes(availableBytes)} | ${totalBytes > 0 ? PulseApp.utils.formatBytes(totalBytes) : 'N/A'} | ${totalBytes > 0 ? PulseApp.utils.createProgressTextBarHTML(usagePercent, usageText, usageColor) : '-'} | ${gcStatusHtml} | `;
- dsTableBody.appendChild(row);
+
+ const createCell = (content, classNames = [], isHtml = false) => {
+ const cell = row.insertCell();
+ cell.className = `${CSS_CLASSES.P1_PX2} ${CSS_CLASSES.WHITESPACE_NOWRAP} ${classNames.join(' ')}`;
+ if (isHtml) {
+ cell.innerHTML = content;
+ } else {
+ cell.textContent = content;
+ }
+ return cell;
+ };
+
+ createCell(ds.name || 'N/A');
+ createCell(ds.path || 'N/A', [CSS_CLASSES.TEXT_GRAY_500_DARK_GRAY_400]);
+ createCell(PulseApp.utils.formatBytes(usedBytes));
+ createCell(PulseApp.utils.formatBytes(availableBytes));
+ createCell(totalBytes > 0 ? PulseApp.utils.formatBytes(totalBytes) : 'N/A');
+
+ const usageCell = createCell(totalBytes > 0 ? PulseApp.utils.createProgressTextBarHTML(usagePercent, usageText, usageColor) : '-', [], true);
+ usageCell.style.minWidth = '150px';
+
+ createCell(gcStatusHtml, [], true);
});
}
} else {
- dsTableBody.innerHTML = `| ${statusText} |
`;
+ const row = dsTableBody.insertRow();
+ const cell = row.insertCell();
+ cell.colSpan = 7;
+ cell.className = `px-4 py-4 ${CSS_CLASSES.TEXT_SM} ${CSS_CLASSES.TEXT_GRAY_400} text-center`;
+ cell.textContent = statusText;
}
};
@@ -327,48 +376,96 @@ PulseApp.ui.pbs = (() => {
const lastOk = PulseApp.utils.formatPbsTimestamp(summary.lastOk);
const lastFailed = PulseApp.utils.formatPbsTimestamp(summary.lastFailed);
const failedStyle = (failed > 0) ? `${CSS_CLASSES.FONT_BOLD} ${CSS_CLASSES.TEXT_RED_600_DARK_RED_400}` : `${CSS_CLASSES.TEXT_RED_600_DARK_RED_400} ${CSS_CLASSES.FONT_SEMIBOLD}`;
-
const highlightClass = (failed > 0) ? CSS_CLASSES.BORDER_L_4_RED_500_DARK_RED_400 : CSS_CLASSES.BORDER_L_4_TRANSPARENT;
+
card.className = `${CSS_CLASSES.BORDER_GRAY_200_DARK_BORDER_GRAY_700} ${CSS_CLASSES.ROUNDED} ${CSS_CLASSES.P3} ${CSS_CLASSES.BG_GRAY_100_50_DARK_BG_GRAY_700_50} ${highlightClass}`;
- card.innerHTML = `
- ${title} (7d)
-
-
OK: ${ok}
-
Failed: ${failed}
-
Total: ${total}
-
Last OK: ${lastOk}
-
Last Fail: ${lastFailed}
-
`;
+ const heading = document.createElement('h4');
+ heading.className = `${CSS_CLASSES.TEXT_MD} ${CSS_CLASSES.FONT_SEMIBOLD} ${CSS_CLASSES.MB2} ${CSS_CLASSES.TEXT_GRAY_700_DARK_GRAY_300}`;
+ heading.textContent = `${title} (7d)`;
+ card.appendChild(heading);
+
+ const contentDiv = document.createElement('div');
+ contentDiv.className = `${CSS_CLASSES.SPACE_Y_1} ${CSS_CLASSES.TEXT_SM}`;
+
+ const createStatDiv = (label, value, valueClass = '') => {
+ const div = document.createElement('div');
+ const labelSpan = document.createElement('span');
+ labelSpan.className = `${CSS_CLASSES.FONT_MEDIUM} ${CSS_CLASSES.TEXT_GRAY_800_DARK_GRAY_200}`;
+ labelSpan.textContent = `${label}:`;
+ div.appendChild(labelSpan);
+
+ const valueSpan = document.createElement('span');
+ valueSpan.className = `${CSS_CLASSES.ML1} ${valueClass}`;
+ valueSpan.textContent = value;
+ div.appendChild(valueSpan);
+ return div;
+ };
+
+ contentDiv.appendChild(createStatDiv('OK', ok, `${CSS_CLASSES.TEXT_GREEN_600_DARK_GREEN_400} ${CSS_CLASSES.FONT_SEMIBOLD}`));
+ contentDiv.appendChild(createStatDiv('Failed', failed, failedStyle));
+ contentDiv.appendChild(createStatDiv('Total', total, `${CSS_CLASSES.TEXT_GRAY_700_DARK_GRAY_300} ${CSS_CLASSES.FONT_SEMIBOLD}`));
+ contentDiv.appendChild(createStatDiv('Last OK', lastOk, `${CSS_CLASSES.TEXT_GRAY_600_DARK_GRAY_400} ${CSS_CLASSES.TEXT_XS}`));
+ contentDiv.appendChild(createStatDiv('Last Fail', lastFailed, `${CSS_CLASSES.TEXT_GRAY_600_DARK_GRAY_400} ${CSS_CLASSES.TEXT_XS}`));
+
+ card.appendChild(contentDiv);
return card;
};
- const _createTaskTableHTML = (tableId, title, idColumnHeader) => {
- const tbodyId = tableId.replace('-table-', '-tbody-'); // Keep this for now as it's specific
- const toggleButtonContainerId = tableId.replace('-table', '-toggle-container'); // Keep this for now
- return `
- Recent ${title} Tasks
-
-
-
-
- | ${idColumnHeader} |
- Status |
- Start Time |
- Duration |
- UPID |
-
-
-
-
-
-
-
-
- `;
+ const _createTaskTableElement = (tableId, title, idColumnHeader) => {
+ const fragment = document.createDocumentFragment();
+
+ const heading = document.createElement('h4');
+ heading.className = `${CSS_CLASSES.TEXT_MD} ${CSS_CLASSES.FONT_SEMIBOLD} ${CSS_CLASSES.MB2} ${CSS_CLASSES.TEXT_GRAY_700_DARK_GRAY_300}`;
+ heading.textContent = `Recent ${title} Tasks`;
+ fragment.appendChild(heading);
+
+ const tableContainer = document.createElement('div');
+ tableContainer.className = `${CSS_CLASSES.OVERFLOW_X_AUTO} ${CSS_CLASSES.BORDER_GRAY_200_DARK_BORDER_GRAY_700} ${CSS_CLASSES.ROUNDED}`;
+
+ const table = document.createElement('table');
+ table.id = tableId;
+ table.className = `${CSS_CLASSES.MIN_W_FULL} ${CSS_CLASSES.DIVIDE_Y_GRAY_200_DARK_DIVIDE_GRAY_700} ${CSS_CLASSES.TEXT_SM}`;
+
+ const thead = document.createElement('thead');
+ thead.className = `${CSS_CLASSES.STICKY} ${CSS_CLASSES.TOP_0} ${CSS_CLASSES.Z_10} ${CSS_CLASSES.BG_GRAY_100_DARK_BG_GRAY_800}`;
+ const headerRow = document.createElement('tr');
+ headerRow.className = `${CSS_CLASSES.TEXT_XS} ${CSS_CLASSES.FONT_MEDIUM} ${CSS_CLASSES.TRACKING_WIDER} ${CSS_CLASSES.TEXT_LEFT} ${CSS_CLASSES.TEXT_GRAY_600_UPPERCASE_DARK_TEXT_GRAY_300} ${CSS_CLASSES.BORDER_B_GRAY_300_DARK_BORDER_GRAY_600}`;
+
+ const headers = [idColumnHeader, 'Status', 'Start Time', 'Duration', 'UPID'];
+ headers.forEach(text => {
+ const th = document.createElement('th');
+ th.scope = 'col';
+ th.className = CSS_CLASSES.P1_PX2;
+ th.textContent = text;
+ headerRow.appendChild(th);
+ });
+ thead.appendChild(headerRow);
+ table.appendChild(thead);
+
+ const tbody = document.createElement('tbody');
+ tbody.id = tableId.replace('-table-', '-tbody-');
+ tbody.className = `${CSS_CLASSES.PBS_TASK_TBODY} ${CSS_CLASSES.DIVIDE_Y_GRAY_200_DARK_DIVIDE_GRAY_700}`;
+ table.appendChild(tbody);
+ tableContainer.appendChild(table);
+ fragment.appendChild(tableContainer);
+
+ const toggleButtonContainer = document.createElement('div');
+ toggleButtonContainer.id = tableId.replace('-table', '-toggle-container');
+ toggleButtonContainer.className = `pbs-toggle-button-container ${CSS_CLASSES.PT1} ${CSS_CLASSES.TEXT_RIGHT}`;
+
+ const showMoreButton = document.createElement('button');
+ showMoreButton.className = `${CSS_CLASSES.PBS_SHOW_MORE} ${CSS_CLASSES.TEXT_BLUE_600_HOVER_BLUE_800_DARK_BLUE_400_DARK_HOVER_BLUE_300} ${CSS_CLASSES.HIDDEN}`;
+ showMoreButton.textContent = 'Show More';
+ toggleButtonContainer.appendChild(showMoreButton);
+
+ const noTasksMessage = document.createElement('p');
+ noTasksMessage.className = `${CSS_CLASSES.PBS_NO_TASKS} ${CSS_CLASSES.TEXT_XS} ${CSS_CLASSES.TEXT_GRAY_400} dark:text-gray-500 ${CSS_CLASSES.HIDDEN} ${CSS_CLASSES.ITALIC}`;
+ noTasksMessage.textContent = 'No recent tasks found.';
+ toggleButtonContainer.appendChild(noTasksMessage);
+
+ fragment.appendChild(toggleButtonContainer);
+ return fragment;
};
pbsArray.forEach((pbsInstance, index) => {
@@ -439,15 +536,111 @@ PulseApp.ui.pbs = (() => {
if (health === 'ok') colorClass = CSS_CLASSES.BG_GREEN_500;
else if (health === 'warning') colorClass = CSS_CLASSES.BG_YELLOW_500;
else if (health === 'error') colorClass = CSS_CLASSES.BG_RED_500;
- return ``;
+ const span = document.createElement('span');
+ span.title = title;
+ span.className = `${CSS_CLASSES.INLINE_BLOCK} ${CSS_CLASSES.W_3} ${CSS_CLASSES.H_3} ${colorClass} ${CSS_CLASSES.ROUNDED_FULL} ${CSS_CLASSES.MR2} ${CSS_CLASSES.FLEX_SHRINK_0}`;
+ return span;
+ };
+
+ const _createPbsInstanceElement = (pbsInstanceData, instanceId, instanceName, overallHealth, healthTitle, showDetails, statusText) => {
+ const instanceWrapper = document.createElement('div');
+ instanceWrapper.className = `${CSS_CLASSES.PBS_INSTANCE_SECTION} ${CSS_CLASSES.BORDER_GRAY_200_DARK_BORDER_GRAY_700} ${CSS_CLASSES.ROUNDED} p-4 mb-4 bg-gray-50/30 dark:bg-gray-800/30`;
+ instanceWrapper.id = ID_PREFIXES.PBS_INSTANCE + instanceId;
+
+ const headerDiv = document.createElement('div');
+ headerDiv.className = `${CSS_CLASSES.FLEX} ${CSS_CLASSES.JUSTIFY_BETWEEN} ${CSS_CLASSES.ITEMS_CENTER} ${CSS_CLASSES.MB3}`;
+ const instanceTitleElement = document.createElement('h3');
+ instanceTitleElement.className = `${CSS_CLASSES.TEXT_LG} ${CSS_CLASSES.FONT_SEMIBOLD} ${CSS_CLASSES.TEXT_GRAY_800_DARK_GRAY_200} ${CSS_CLASSES.FLEX} ${CSS_CLASSES.ITEMS_CENTER}`;
+ instanceTitleElement.appendChild(_createHealthBadgeHTML(overallHealth, healthTitle));
+ instanceTitleElement.appendChild(document.createTextNode(instanceName));
+ headerDiv.appendChild(instanceTitleElement);
+ instanceWrapper.appendChild(headerDiv);
+
+ const detailsContainer = document.createElement('div');
+ detailsContainer.className = `${CSS_CLASSES.PBS_INSTANCE_DETAILS} ${CSS_CLASSES.SPACE_Y_4} ${showDetails ? '' : CSS_CLASSES.HIDDEN}`;
+ detailsContainer.id = ID_PREFIXES.PBS_DETAILS + instanceId;
+
+ const dsSection = document.createElement('div');
+ dsSection.id = ID_PREFIXES.PBS_DS_SECTION + instanceId;
+ const dsHeading = document.createElement('h4');
+ dsHeading.className = `${CSS_CLASSES.TEXT_MD} ${CSS_CLASSES.FONT_SEMIBOLD} ${CSS_CLASSES.MB2} ${CSS_CLASSES.TEXT_GRAY_700_DARK_GRAY_300}`;
+ dsHeading.textContent = 'Datastores';
+ dsSection.appendChild(dsHeading);
+ const dsTableContainer = document.createElement('div');
+ dsTableContainer.className = `${CSS_CLASSES.OVERFLOW_X_AUTO} ${CSS_CLASSES.BORDER_GRAY_200_DARK_BORDER_GRAY_700} ${CSS_CLASSES.ROUNDED}`;
+ const dsTable = document.createElement('table');
+ dsTable.id = ID_PREFIXES.PBS_DS_TABLE + instanceId;
+ dsTable.className = `${CSS_CLASSES.MIN_W_FULL} ${CSS_CLASSES.DIVIDE_Y_GRAY_200_DARK_DIVIDE_GRAY_700} ${CSS_CLASSES.TEXT_SM}`;
+ const dsThead = document.createElement('thead');
+ dsThead.className = `${CSS_CLASSES.STICKY} ${CSS_CLASSES.TOP_0} ${CSS_CLASSES.Z_10} ${CSS_CLASSES.BG_GRAY_100_DARK_BG_GRAY_800}`;
+ const dsHeaderRow = document.createElement('tr');
+ dsHeaderRow.className = `${CSS_CLASSES.TEXT_XS} ${CSS_CLASSES.FONT_MEDIUM} ${CSS_CLASSES.TRACKING_WIDER} ${CSS_CLASSES.TEXT_LEFT} ${CSS_CLASSES.TEXT_GRAY_600_UPPERCASE_DARK_TEXT_GRAY_300} ${CSS_CLASSES.BORDER_B_GRAY_300_DARK_BORDER_GRAY_600}`;
+ ['Name', 'Path', 'Used', 'Available', 'Total', 'Usage', 'GC Status'].forEach(headerText => {
+ const th = document.createElement('th');
+ th.scope = 'col';
+ th.className = CSS_CLASSES.P1_PX2;
+ th.textContent = headerText;
+ dsHeaderRow.appendChild(th);
+ });
+ dsThead.appendChild(dsHeaderRow);
+ dsTable.appendChild(dsThead);
+ const dsTbody = document.createElement('tbody');
+ dsTbody.id = ID_PREFIXES.PBS_DS_TBODY + instanceId;
+ dsTbody.className = CSS_CLASSES.DIVIDE_Y_GRAY_200_DARK_DIVIDE_GRAY_700;
+ dsTable.appendChild(dsTbody);
+ dsTableContainer.appendChild(dsTable);
+ dsSection.appendChild(dsTableContainer);
+ detailsContainer.appendChild(dsSection);
+
+ const summariesSection = document.createElement('div');
+ summariesSection.id = ID_PREFIXES.PBS_SUMMARIES_SECTION + instanceId;
+ summariesSection.className = `${CSS_CLASSES.GRID} ${CSS_CLASSES.GRID_COLS_1} ${CSS_CLASSES.MD_GRID_COLS_2} ${CSS_CLASSES.LG_GRID_COLS_4} ${CSS_CLASSES.GAP_4}`;
+ summariesSection.appendChild(_createSummaryCard('backup', 'Backups', pbsInstanceData.backupTasks));
+ summariesSection.appendChild(_createSummaryCard('verify', 'Verification', pbsInstanceData.verificationTasks));
+ summariesSection.appendChild(_createSummaryCard('sync', 'Sync', pbsInstanceData.syncTasks));
+ summariesSection.appendChild(_createSummaryCard('prune', 'Prune/GC', pbsInstanceData.pruneTasks));
+ detailsContainer.appendChild(summariesSection);
+
+ const recentBackupTasksSection = document.createElement('div');
+ recentBackupTasksSection.className = CSS_CLASSES.PBS_TASK_SECTION;
+ recentBackupTasksSection.dataset.taskType = 'backup';
+ recentBackupTasksSection.appendChild(_createTaskTableElement(ID_PREFIXES.PBS_RECENT_BACKUP_TASKS_TABLE + instanceId, 'Backup', 'Guest'));
+ detailsContainer.appendChild(recentBackupTasksSection);
+
+ const recentVerifyTasksSection = document.createElement('div');
+ recentVerifyTasksSection.className = CSS_CLASSES.PBS_TASK_SECTION;
+ recentVerifyTasksSection.dataset.taskType = 'verify';
+ recentVerifyTasksSection.appendChild(_createTaskTableElement(ID_PREFIXES.PBS_RECENT_VERIFY_TASKS_TABLE + instanceId, 'Verification', 'Guest/Group'));
+ detailsContainer.appendChild(recentVerifyTasksSection);
+
+ const recentSyncTasksSection = document.createElement('div');
+ recentSyncTasksSection.className = CSS_CLASSES.PBS_TASK_SECTION;
+ recentSyncTasksSection.dataset.taskType = 'sync';
+ recentSyncTasksSection.appendChild(_createTaskTableElement(ID_PREFIXES.PBS_RECENT_SYNC_TASKS_TABLE + instanceId, 'Sync', 'Job ID'));
+ detailsContainer.appendChild(recentSyncTasksSection);
+
+ const recentPruneGcTasksSection = document.createElement('div');
+ recentPruneGcTasksSection.className = CSS_CLASSES.PBS_TASK_SECTION;
+ recentPruneGcTasksSection.dataset.taskType = 'prunegc';
+ recentPruneGcTasksSection.appendChild(_createTaskTableElement(ID_PREFIXES.PBS_RECENT_PRUNEGC_TASKS_TABLE + instanceId, 'Prune/GC', 'Datastore/Group'));
+ detailsContainer.appendChild(recentPruneGcTasksSection);
+
+ instanceWrapper.appendChild(detailsContainer);
+
+ const dsTableBodyElement = instanceWrapper.querySelector(`#${ID_PREFIXES.PBS_DS_TBODY}${instanceId}`);
+ _populateDsTableBody(dsTableBodyElement, pbsInstanceData.datastores, statusText, showDetails);
+ _populateInstanceTaskSections(detailsContainer, instanceId, pbsInstanceData, statusText, showDetails);
+
+ return instanceWrapper;
};
if (instanceWrapper) {
detailsContainer = instanceWrapper.querySelector(`#${ID_PREFIXES.PBS_DETAILS}${instanceId}`);
instanceTitleElement = instanceWrapper.querySelector('h3');
-
if (instanceTitleElement) {
- instanceTitleElement.innerHTML = `${_createHealthBadgeHTML(overallHealth, healthTitle)}${instanceName}`;
+ instanceTitleElement.innerHTML = ''; // Clear existing content
+ instanceTitleElement.appendChild(_createHealthBadgeHTML(overallHealth, healthTitle));
+ instanceTitleElement.appendChild(document.createTextNode(instanceName));
}
if (detailsContainer) {
@@ -466,87 +659,10 @@ PulseApp.ui.pbs = (() => {
_populateInstanceTaskSections(detailsContainer, instanceId, pbsInstance, statusText, showDetails);
detailsContainer.classList.toggle(CSS_CLASSES.HIDDEN, !showDetails);
}
-
} else {
- instanceWrapper = document.createElement('div');
- instanceWrapper.className = `${CSS_CLASSES.PBS_INSTANCE_SECTION} ${CSS_CLASSES.BORDER_GRAY_200_DARK_BORDER_GRAY_700} ${CSS_CLASSES.ROUNDED} p-4 mb-4 bg-gray-50/30 dark:bg-gray-800/30`;
- instanceWrapper.id = instanceElementId;
-
- const headerDiv = document.createElement('div');
- headerDiv.className = `${CSS_CLASSES.FLEX} ${CSS_CLASSES.JUSTIFY_BETWEEN} ${CSS_CLASSES.ITEMS_CENTER} ${CSS_CLASSES.MB3}`;
- instanceTitleElement = document.createElement('h3');
- instanceTitleElement.className = `${CSS_CLASSES.TEXT_LG} ${CSS_CLASSES.FONT_SEMIBOLD} ${CSS_CLASSES.TEXT_GRAY_800_DARK_GRAY_200} ${CSS_CLASSES.FLEX} ${CSS_CLASSES.ITEMS_CENTER}`;
- instanceTitleElement.innerHTML = `${_createHealthBadgeHTML(overallHealth, healthTitle)}${instanceName}`;
- headerDiv.appendChild(instanceTitleElement);
- instanceWrapper.appendChild(headerDiv);
-
- detailsContainer = document.createElement('div');
- detailsContainer.className = `${CSS_CLASSES.PBS_INSTANCE_DETAILS} ${CSS_CLASSES.SPACE_Y_4} ${showDetails ? '' : CSS_CLASSES.HIDDEN}`;
- detailsContainer.id = ID_PREFIXES.PBS_DETAILS + instanceId;
-
- const dsSection = document.createElement('div');
- dsSection.id = ID_PREFIXES.PBS_DS_SECTION + instanceId;
- dsSection.innerHTML = `
- Datastores
-
-
-
-
- | Name |
- Path |
- Used |
- Available |
- Total |
- Usage |
- GC Status |
-
-
-
-
-
`;
- detailsContainer.appendChild(dsSection);
-
- const summariesSection = document.createElement('div');
- summariesSection.id = ID_PREFIXES.PBS_SUMMARIES_SECTION + instanceId;
- summariesSection.className = `${CSS_CLASSES.GRID} ${CSS_CLASSES.GRID_COLS_1} ${CSS_CLASSES.MD_GRID_COLS_2} ${CSS_CLASSES.LG_GRID_COLS_4} ${CSS_CLASSES.GAP_4}`;
- summariesSection.appendChild(_createSummaryCard('backup', 'Backups', pbsInstance.backupTasks));
- summariesSection.appendChild(_createSummaryCard('verify', 'Verification', pbsInstance.verificationTasks));
- summariesSection.appendChild(_createSummaryCard('sync', 'Sync', pbsInstance.syncTasks));
- summariesSection.appendChild(_createSummaryCard('prune', 'Prune/GC', pbsInstance.pruneTasks));
- detailsContainer.appendChild(summariesSection);
-
- const recentBackupTasksSection = document.createElement('div');
- recentBackupTasksSection.className = CSS_CLASSES.PBS_TASK_SECTION;
- recentBackupTasksSection.dataset.taskType = 'backup';
- recentBackupTasksSection.innerHTML = _createTaskTableHTML(ID_PREFIXES.PBS_RECENT_BACKUP_TASKS_TABLE + instanceId, 'Backup', 'Guest');
- detailsContainer.appendChild(recentBackupTasksSection);
-
- const recentVerifyTasksSection = document.createElement('div');
- recentVerifyTasksSection.className = CSS_CLASSES.PBS_TASK_SECTION;
- recentVerifyTasksSection.dataset.taskType = 'verify';
- recentVerifyTasksSection.innerHTML = _createTaskTableHTML(ID_PREFIXES.PBS_RECENT_VERIFY_TASKS_TABLE + instanceId, 'Verification', 'Guest/Group');
- detailsContainer.appendChild(recentVerifyTasksSection);
-
- const recentSyncTasksSection = document.createElement('div');
- recentSyncTasksSection.className = CSS_CLASSES.PBS_TASK_SECTION;
- recentSyncTasksSection.dataset.taskType = 'sync';
- recentSyncTasksSection.innerHTML = _createTaskTableHTML(ID_PREFIXES.PBS_RECENT_SYNC_TASKS_TABLE + instanceId, 'Sync', 'Job ID');
- detailsContainer.appendChild(recentSyncTasksSection);
-
- const recentPruneGcTasksSection = document.createElement('div');
- recentPruneGcTasksSection.className = CSS_CLASSES.PBS_TASK_SECTION;
- recentPruneGcTasksSection.dataset.taskType = 'prunegc';
- recentPruneGcTasksSection.innerHTML = _createTaskTableHTML(ID_PREFIXES.PBS_RECENT_PRUNEGC_TASKS_TABLE + instanceId, 'Prune/GC', 'Datastore/Group');
- detailsContainer.appendChild(recentPruneGcTasksSection);
-
- instanceWrapper.appendChild(detailsContainer);
+ instanceWrapper = _createPbsInstanceElement(pbsInstance, instanceId, instanceName, overallHealth, healthTitle, showDetails, statusText);
container.appendChild(instanceWrapper);
-
- dsTableBody = instanceWrapper.querySelector(`#${ID_PREFIXES.PBS_DS_TBODY}${instanceId}`);
- _populateDsTableBody(dsTableBody, pbsInstance.datastores, statusText, showDetails);
- _populateInstanceTaskSections(detailsContainer, instanceId, pbsInstance, statusText, showDetails);
}
-
});
container.querySelectorAll(`.${CSS_CLASSES.PBS_INSTANCE_SECTION}`).forEach(el => {