mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-23 19:57:09 +00:00
refactor: Consolidate PBS task summaries into a single table
This commit is contained in:
+191
-152
@@ -84,6 +84,7 @@ PulseApp.ui.pbs = (() => {
|
||||
BORDER_T_2: 'border-t-2',
|
||||
BORDER_B_2: 'border-b-2',
|
||||
BORDER_BLUE_500: 'border-blue-500',
|
||||
BG_GRAY_50_DARK_BG_GRAY_800_30: 'bg-gray-50 dark:bg-gray-800/30',
|
||||
};
|
||||
|
||||
const ID_PREFIXES = {
|
||||
@@ -338,6 +339,61 @@ PulseApp.ui.pbs = (() => {
|
||||
}
|
||||
};
|
||||
|
||||
const _createHealthBadgeHTML = (health, title) => {
|
||||
let colorClass = CSS_CLASSES.BG_GRAY_400_DARK_BG_GRAY_500;
|
||||
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;
|
||||
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 _createInstanceHeaderDiv = (instanceName, overallHealth, healthTitle) => {
|
||||
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);
|
||||
return headerDiv;
|
||||
};
|
||||
|
||||
const _createDatastoreSectionElement = (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);
|
||||
return dsSection;
|
||||
};
|
||||
|
||||
function updatePbsInfo(pbsArray) {
|
||||
const container = document.getElementById(ID_PREFIXES.PBS_INSTANCES_CONTAINER);
|
||||
@@ -367,49 +423,96 @@ PulseApp.ui.pbs = (() => {
|
||||
|
||||
const currentInstanceIds = new Set();
|
||||
|
||||
const _createSummaryCard = (type, title, summaryData) => {
|
||||
const card = document.createElement('div');
|
||||
const summary = summaryData?.summary || {};
|
||||
const ok = summary.ok ?? '-';
|
||||
const failed = summary.failed ?? '-';
|
||||
const total = summary.total ?? '-';
|
||||
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}`;
|
||||
const _createPbsTaskHealthTable = (instanceId, pbsInstanceData) => {
|
||||
const sectionDiv = document.createElement('div');
|
||||
sectionDiv.id = ID_PREFIXES.PBS_SUMMARIES_SECTION + instanceId;
|
||||
// Add a general class for styling this new section if needed, e.g., 'pbs-task-health-section'
|
||||
sectionDiv.className = `pbs-task-health-section ${CSS_CLASSES.MB3}`; // Added margin-bottom for spacing
|
||||
|
||||
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);
|
||||
heading.textContent = 'PBS Task Health (14d)';
|
||||
sectionDiv.appendChild(heading);
|
||||
|
||||
const contentDiv = document.createElement('div');
|
||||
contentDiv.className = `${CSS_CLASSES.SPACE_Y_1} ${CSS_CLASSES.TEXT_SM}`;
|
||||
const tableContainer = document.createElement('div');
|
||||
// Make the tableContainer itself look more like a card
|
||||
tableContainer.className = `${CSS_CLASSES.OVERFLOW_X_AUTO} ${CSS_CLASSES.BORDER_GRAY_200_DARK_BORDER_GRAY_700} ${CSS_CLASSES.ROUNDED} ${CSS_CLASSES.BG_GRAY_50_DARK_BG_GRAY_800_30} p-3`; // Added background and padding
|
||||
|
||||
const table = document.createElement('table');
|
||||
table.className = `${CSS_CLASSES.MIN_W_FULL} ${CSS_CLASSES.TEXT_SM}`;
|
||||
// Removed DIVIDE_Y from table itself, will rely on cell padding and borders if needed
|
||||
|
||||
const thead = document.createElement('thead');
|
||||
thead.className = `${CSS_CLASSES.BG_GRAY_100_DARK_BG_GRAY_800}`; // Added background to header
|
||||
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 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 headers = ['Task Type', 'Status', 'Last Successful Run', 'Last Failure'];
|
||||
headers.forEach(text => {
|
||||
const th = document.createElement('th');
|
||||
th.scope = 'col';
|
||||
th.className = `${CSS_CLASSES.P1_PX2} ${CSS_CLASSES.TEXT_LEFT}`; // Ensure left alignment for headers
|
||||
th.textContent = text;
|
||||
headerRow.appendChild(th);
|
||||
});
|
||||
thead.appendChild(headerRow);
|
||||
table.appendChild(thead);
|
||||
|
||||
const valueSpan = document.createElement('span');
|
||||
valueSpan.className = `${CSS_CLASSES.ML1} ${valueClass}`;
|
||||
valueSpan.textContent = value;
|
||||
div.appendChild(valueSpan);
|
||||
return div;
|
||||
};
|
||||
const tbody = document.createElement('tbody');
|
||||
tbody.className = `pbs-task-health-tbody`;
|
||||
|
||||
// Data for the table rows
|
||||
const taskHealthData = [
|
||||
{ title: 'Backups', data: pbsInstanceData.backupTasks },
|
||||
{ title: 'Verification', data: pbsInstanceData.verificationTasks },
|
||||
{ title: 'Sync', data: pbsInstanceData.syncTasks },
|
||||
{ title: 'Prune/GC', data: pbsInstanceData.pruneTasks }
|
||||
];
|
||||
|
||||
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}`));
|
||||
taskHealthData.forEach(taskItem => {
|
||||
const summary = taskItem.data?.summary || {};
|
||||
const ok = summary.ok ?? '-';
|
||||
const failed = summary.failed ?? 0; // Default to 0 for comparison
|
||||
const total = summary.total ?? '-';
|
||||
const lastOk = PulseApp.utils.formatPbsTimestamp(summary.lastOk);
|
||||
const lastFailed = PulseApp.utils.formatPbsTimestamp(summary.lastFailed);
|
||||
|
||||
card.appendChild(contentDiv);
|
||||
return card;
|
||||
const row = tbody.insertRow();
|
||||
// Remove left border highlighting - rely on text styling in Status cell
|
||||
// if (failed > 0) {
|
||||
// row.className = CSS_CLASSES.BORDER_L_4_RED_500_DARK_RED_400;
|
||||
// } else {
|
||||
// row.className = CSS_CLASSES.BORDER_L_4_TRANSPARENT;
|
||||
// }
|
||||
|
||||
// Ensure row still has its bottom border for separation
|
||||
row.className = 'initial-row-class-if-any'; // Clear previous BORDER_L classes
|
||||
row.classList.add(...CSS_CLASSES.BORDER_B_GRAY_200_DARK_GRAY_700.split(' '));
|
||||
|
||||
const cellTaskType = row.insertCell();
|
||||
cellTaskType.className = `${CSS_CLASSES.P1_PX2} ${CSS_CLASSES.FONT_SEMIBOLD} ${CSS_CLASSES.TEXT_GRAY_800_DARK_GRAY_200}`;
|
||||
cellTaskType.textContent = taskItem.title;
|
||||
|
||||
const cellStatus = row.insertCell();
|
||||
cellStatus.className = CSS_CLASSES.P1_PX2;
|
||||
const okHtml = `<span class="${CSS_CLASSES.TEXT_GREEN_600_DARK_GREEN_400}">${ok} OK</span>`;
|
||||
const failHtml = `<span class="${failed > 0 ? CSS_CLASSES.FONT_BOLD + ' ' + CSS_CLASSES.TEXT_RED_600_DARK_RED_400 : CSS_CLASSES.TEXT_GRAY_600_DARK_GRAY_400}">${failed} Fail</span>`;
|
||||
cellStatus.innerHTML = `${okHtml} / ${failHtml}`;
|
||||
|
||||
const cellLastOk = row.insertCell();
|
||||
cellLastOk.className = `${CSS_CLASSES.P1_PX2} ${CSS_CLASSES.TEXT_GRAY_600_DARK_GRAY_400}`;
|
||||
cellLastOk.textContent = lastOk;
|
||||
|
||||
const cellLastFail = row.insertCell();
|
||||
cellLastFail.className = `${CSS_CLASSES.P1_PX2} ${CSS_CLASSES.TEXT_GRAY_600_DARK_GRAY_400}`;
|
||||
cellLastFail.textContent = lastFailed;
|
||||
});
|
||||
|
||||
table.appendChild(tbody);
|
||||
tableContainer.appendChild(table);
|
||||
sectionDiv.appendChild(tableContainer);
|
||||
|
||||
return sectionDiv;
|
||||
};
|
||||
|
||||
const _createTaskTableElement = (tableId, title, idColumnHeader) => {
|
||||
@@ -468,6 +571,58 @@ PulseApp.ui.pbs = (() => {
|
||||
return fragment;
|
||||
};
|
||||
|
||||
const _createSummariesSectionElement = (instanceId, pbsInstanceData) => {
|
||||
// This function is now a wrapper for _createPbsTaskHealthTable
|
||||
// The old grid layout is replaced by the single table.
|
||||
return _createPbsTaskHealthTable(instanceId, pbsInstanceData);
|
||||
};
|
||||
|
||||
const _createAllTaskSectionsContainer = (instanceId) => {
|
||||
const container = document.createElement('div'); // Or DocumentFragment
|
||||
container.className = CSS_CLASSES.SPACE_Y_4; // Add some spacing between task sections
|
||||
|
||||
const taskDefinitions = [
|
||||
{ type: 'backup', title: 'Backup', idCol: 'Guest', tableIdPrefix: ID_PREFIXES.PBS_RECENT_BACKUP_TASKS_TABLE },
|
||||
{ type: 'verify', title: 'Verification', idCol: 'Guest/Group', tableIdPrefix: ID_PREFIXES.PBS_RECENT_VERIFY_TASKS_TABLE },
|
||||
{ type: 'sync', title: 'Sync', idCol: 'Job ID', tableIdPrefix: ID_PREFIXES.PBS_RECENT_SYNC_TASKS_TABLE },
|
||||
{ type: 'prunegc', title: 'Prune/GC', idCol: 'Datastore/Group', tableIdPrefix: ID_PREFIXES.PBS_RECENT_PRUNEGC_TASKS_TABLE }
|
||||
];
|
||||
|
||||
taskDefinitions.forEach(def => {
|
||||
const taskSection = document.createElement('div');
|
||||
taskSection.className = CSS_CLASSES.PBS_TASK_SECTION;
|
||||
taskSection.dataset.taskType = def.type;
|
||||
taskSection.appendChild(_createTaskTableElement(def.tableIdPrefix + instanceId, def.title, def.idCol));
|
||||
container.appendChild(taskSection);
|
||||
});
|
||||
return container;
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
instanceWrapper.appendChild(_createInstanceHeaderDiv(instanceName, overallHealth, healthTitle));
|
||||
|
||||
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;
|
||||
|
||||
detailsContainer.appendChild(_createDatastoreSectionElement(instanceId));
|
||||
detailsContainer.appendChild(_createSummariesSectionElement(instanceId, pbsInstanceData));
|
||||
detailsContainer.appendChild(_createAllTaskSectionsContainer(instanceId));
|
||||
|
||||
instanceWrapper.appendChild(detailsContainer);
|
||||
|
||||
// Populate dynamic content after structure is built
|
||||
const dsTableBodyElement = instanceWrapper.querySelector(`#${ID_PREFIXES.PBS_DS_TBODY}${instanceId}`);
|
||||
_populateDsTableBody(dsTableBodyElement, pbsInstanceData.datastores, statusText, showDetails);
|
||||
_populateInstanceTaskSections(detailsContainer, instanceId, pbsInstanceData, statusText, showDetails);
|
||||
|
||||
return instanceWrapper;
|
||||
};
|
||||
|
||||
pbsArray.forEach((pbsInstance, index) => {
|
||||
const rawInstanceId = pbsInstance.pbsEndpointId || `instance-${index}`;
|
||||
const instanceId = PulseApp.utils.sanitizeForId(rawInstanceId);
|
||||
@@ -531,119 +686,6 @@ PulseApp.ui.pbs = (() => {
|
||||
}
|
||||
}
|
||||
|
||||
const _createHealthBadgeHTML = (health, title) => {
|
||||
let colorClass = CSS_CLASSES.BG_GRAY_400_DARK_BG_GRAY_500;
|
||||
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;
|
||||
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 _createInstanceHeaderDiv = (instanceName, overallHealth, healthTitle) => {
|
||||
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);
|
||||
return headerDiv;
|
||||
};
|
||||
|
||||
const _createDatastoreSectionElement = (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);
|
||||
return dsSection;
|
||||
};
|
||||
|
||||
const _createSummariesSectionElement = (instanceId, pbsInstanceData) => {
|
||||
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));
|
||||
return summariesSection;
|
||||
};
|
||||
|
||||
const _createAllTaskSectionsContainer = (instanceId) => {
|
||||
const container = document.createElement('div'); // Or DocumentFragment
|
||||
container.className = CSS_CLASSES.SPACE_Y_4; // Add some spacing between task sections
|
||||
|
||||
const taskDefinitions = [
|
||||
{ type: 'backup', title: 'Backup', idCol: 'Guest', tableIdPrefix: ID_PREFIXES.PBS_RECENT_BACKUP_TASKS_TABLE },
|
||||
{ type: 'verify', title: 'Verification', idCol: 'Guest/Group', tableIdPrefix: ID_PREFIXES.PBS_RECENT_VERIFY_TASKS_TABLE },
|
||||
{ type: 'sync', title: 'Sync', idCol: 'Job ID', tableIdPrefix: ID_PREFIXES.PBS_RECENT_SYNC_TASKS_TABLE },
|
||||
{ type: 'prunegc', title: 'Prune/GC', idCol: 'Datastore/Group', tableIdPrefix: ID_PREFIXES.PBS_RECENT_PRUNEGC_TASKS_TABLE }
|
||||
];
|
||||
|
||||
taskDefinitions.forEach(def => {
|
||||
const taskSection = document.createElement('div');
|
||||
taskSection.className = CSS_CLASSES.PBS_TASK_SECTION;
|
||||
taskSection.dataset.taskType = def.type;
|
||||
taskSection.appendChild(_createTaskTableElement(def.tableIdPrefix + instanceId, def.title, def.idCol));
|
||||
container.appendChild(taskSection);
|
||||
});
|
||||
return container;
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
instanceWrapper.appendChild(_createInstanceHeaderDiv(instanceName, overallHealth, healthTitle));
|
||||
|
||||
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;
|
||||
|
||||
detailsContainer.appendChild(_createDatastoreSectionElement(instanceId));
|
||||
detailsContainer.appendChild(_createSummariesSectionElement(instanceId, pbsInstanceData));
|
||||
detailsContainer.appendChild(_createAllTaskSectionsContainer(instanceId));
|
||||
|
||||
instanceWrapper.appendChild(detailsContainer);
|
||||
|
||||
// Populate dynamic content after structure is built
|
||||
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');
|
||||
@@ -660,10 +702,7 @@ PulseApp.ui.pbs = (() => {
|
||||
const summariesSection = detailsContainer.querySelector(`#${ID_PREFIXES.PBS_SUMMARIES_SECTION}${instanceId}`);
|
||||
if (summariesSection) {
|
||||
summariesSection.innerHTML = '';
|
||||
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));
|
||||
summariesSection.appendChild(_createPbsTaskHealthTable(instanceId, pbsInstance));
|
||||
}
|
||||
|
||||
_populateInstanceTaskSections(detailsContainer, instanceId, pbsInstance, statusText, showDetails);
|
||||
|
||||
Reference in New Issue
Block a user