Skip to content

Commit caf6a2b

Browse files
committed
Update public HTML and existing JS files
1 parent f605f49 commit caf6a2b

File tree

8 files changed

+281
-57
lines changed

8 files changed

+281
-57
lines changed

src/public/index.html

Lines changed: 29 additions & 21 deletions
Large diffs are not rendered by default.

src/public/js/tabs.js

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ PulseApp.ui.tabs = (() => {
77
let nestedTabContentContainer = null;
88
let mainTabsContainer = null;
99
let logSessionArea = null;
10+
let loadedTabs = new Set();
1011

1112
function init() {
1213
tabs = Array.from(document.querySelectorAll('.tab'));
@@ -40,19 +41,40 @@ PulseApp.ui.tabs = (() => {
4041
}
4142
});
4243
}
44+
45+
// Mark main tab as loaded since it's shown by default
46+
loadedTabs.add('main');
4347
}
4448

4549
function activateMainTab(clickedTab, tabId) {
4650
if (clickedTab.classList.contains('pointer-events-none')) return;
4751

48-
tabs.forEach(t => styleMainTab(t, false)); // Use the helper
49-
tabContents.forEach(content => content.classList.add('hidden'));
52+
tabs.forEach(t => {
53+
styleMainTab(t, false);
54+
// Update ARIA attributes
55+
t.setAttribute('aria-selected', 'false');
56+
t.setAttribute('tabindex', '-1');
57+
});
58+
tabContents.forEach(content => {
59+
content.classList.add('hidden');
60+
content.setAttribute('aria-hidden', 'true');
61+
});
5062

51-
styleMainTab(clickedTab, true); // Use the helper
63+
styleMainTab(clickedTab, true);
64+
// Update ARIA attributes for active tab
65+
clickedTab.setAttribute('aria-selected', 'true');
66+
clickedTab.setAttribute('tabindex', '0');
5267

5368
const activeContent = document.getElementById(tabId);
5469
if (activeContent) {
5570
activeContent.classList.remove('hidden');
71+
activeContent.setAttribute('aria-hidden', 'false');
72+
73+
// Lazy load tab content
74+
if (!loadedTabs.has(tabId)) {
75+
loadTabContent(tabId);
76+
loadedTabs.add(tabId);
77+
}
5678

5779
if (tabId === 'main') {
5880
activateNestedTab('nested-tab-dashboard');
@@ -70,6 +92,18 @@ PulseApp.ui.tabs = (() => {
7092
console.warn('[Tabs] PulseApp.ui.backups not available for updateBackupsTab')
7193
}
7294
}
95+
96+
if (tabId === 'storage') {
97+
if (PulseApp.ui && PulseApp.ui.storage) {
98+
PulseApp.ui.storage.updateStorageTab();
99+
}
100+
}
101+
102+
if (tabId === 'pbs') {
103+
if (PulseApp.ui && PulseApp.ui.pbs) {
104+
PulseApp.ui.pbs.updatePBSTab();
105+
}
106+
}
73107
}
74108
}
75109

@@ -349,9 +383,38 @@ PulseApp.ui.tabs = (() => {
349383
}
350384
}
351385

386+
function loadTabContent(tabId) {
387+
// Load tab-specific data only when the tab is first accessed
388+
switch(tabId) {
389+
case 'main':
390+
// Main tab is loaded by default
391+
break;
392+
case 'storage':
393+
if (PulseApp.ui && PulseApp.ui.storage) {
394+
console.log(`[Tabs] Lazy loading ${tabId} content`);
395+
// Storage data is already fetched, just update the UI
396+
}
397+
break;
398+
case 'backups':
399+
if (PulseApp.ui && PulseApp.ui.backups) {
400+
console.log(`[Tabs] Lazy loading ${tabId} content`);
401+
// Backups data is fetched on demand in updateBackupsTab
402+
}
403+
break;
404+
case 'pbs':
405+
if (PulseApp.ui && PulseApp.ui.pbs) {
406+
console.log(`[Tabs] Lazy loading ${tabId} content`);
407+
// PBS data is already fetched, just update the UI
408+
}
409+
break;
410+
}
411+
}
412+
352413
return {
353414
init,
354415
activateNestedTab,
355-
updateTabAvailability
416+
updateTabAvailability,
417+
addLogTab,
418+
removeLogTabAndContent
356419
};
357420
})();

src/public/js/tooltips.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,20 @@ PulseApp.tooltips = (() => {
2727
}
2828

2929
function handleMouseOver(event) {
30-
const target = event.target.closest('.metric-tooltip-trigger, .storage-tooltip-trigger');
30+
const target = event.target.closest('[data-tooltip], .metric-tooltip-trigger, .storage-tooltip-trigger, .truncate');
3131
if (target) {
32-
const tooltipText = target.getAttribute('data-tooltip');
32+
let tooltipText = target.getAttribute('data-tooltip');
33+
34+
// Auto-generate tooltip for truncated text
35+
if (!tooltipText && target.classList.contains('truncate')) {
36+
const fullText = target.textContent.trim();
37+
const title = target.getAttribute('title');
38+
// Only show tooltip if text is actually truncated
39+
if ((title && title !== fullText) || target.scrollWidth > target.clientWidth) {
40+
tooltipText = title || fullText;
41+
}
42+
}
43+
3344
if (tooltipText && tooltipElement) {
3445
tooltipElement.textContent = tooltipText;
3546
positionTooltip(event);

src/public/js/ui/backups.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@ PulseApp.ui.backups = (() => {
293293
const loadingMsg = document.getElementById('backups-loading-message');
294294
const noDataMsg = document.getElementById('backups-no-data-message');
295295
const statusTextElement = document.getElementById('backups-status-text');
296+
const pbsSummaryElement = document.getElementById('pbs-instances-summary');
296297

297298
if (!tableContainer || !tableBody || !loadingMsg || !noDataMsg || !statusTextElement) {
298299
console.error("UI elements for Backups tab not found!");
@@ -321,6 +322,45 @@ PulseApp.ui.backups = (() => {
321322
const backupStatusByGuest = allGuests.map(guest => _determineGuestBackupStatus(guest, snapshotsByGuest.get(`${guest.vmid}-${guest.type === 'qemu' ? 'vm' : 'ct'}`) || [], tasksByGuest.get(`${guest.vmid}-${guest.type === 'qemu' ? 'vm' : 'ct'}`) || [], dayBoundaries, threeDaysAgo, sevenDaysAgo));
322323
const filteredBackupStatus = _filterBackupData(backupStatusByGuest, backupsSearchInput);
323324

325+
// Calculate PBS instances summary - only show if multiple PBS instances
326+
const pbsDataArray = PulseApp.state.get('pbsDataArray') || [];
327+
const pbsSummaryDismissed = PulseApp.state.get('pbsSummaryDismissed') || false;
328+
329+
if (pbsSummaryElement) {
330+
if (pbsDataArray.length > 1 && !pbsSummaryDismissed) {
331+
const pbsSummary = pbsDataArray.map(pbs => {
332+
const backupCount = (pbs.datastores || []).reduce((total, ds) =>
333+
total + (ds.snapshots ? ds.snapshots.length : 0), 0);
334+
return `${pbs.pbsInstanceName}: ${backupCount} backups`;
335+
}).join(' | ');
336+
337+
pbsSummaryElement.innerHTML = `
338+
<div class="flex justify-between items-center">
339+
<div>
340+
<strong>PBS Instances (${pbsDataArray.length}):</strong> ${pbsSummary}
341+
<span class="text-gray-500 dark:text-gray-400 ml-2">• Showing aggregated backup data from all instances</span>
342+
</div>
343+
<button id="dismiss-pbs-summary" class="text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 ml-4" title="Dismiss">
344+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
345+
</button>
346+
</div>
347+
`;
348+
pbsSummaryElement.classList.remove('hidden');
349+
350+
// Add dismiss handler
351+
const dismissBtn = document.getElementById('dismiss-pbs-summary');
352+
if (dismissBtn) {
353+
dismissBtn.addEventListener('click', () => {
354+
pbsSummaryElement.classList.add('hidden');
355+
PulseApp.state.set('pbsSummaryDismissed', true);
356+
PulseApp.state.saveFilterState();
357+
});
358+
}
359+
} else {
360+
pbsSummaryElement.classList.add('hidden');
361+
}
362+
}
363+
324364
const sortStateBackups = PulseApp.state.getSortState('backups');
325365
const sortedBackupStatus = PulseApp.utils.sortData(filteredBackupStatus, sortStateBackups.column, sortStateBackups.direction, 'backups');
326366

src/public/js/ui/common.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,12 @@ PulseApp.ui.common = (() => {
247247
const tableType = tableTypeMatch[1];
248248

249249
tableElement.querySelectorAll('th.sortable').forEach(th => {
250-
th.addEventListener('click', () => {
250+
// Make sortable headers keyboard accessible
251+
th.setAttribute('tabindex', '0');
252+
th.setAttribute('role', 'button');
253+
th.setAttribute('aria-label', `Sort by ${th.textContent.trim()}`);
254+
255+
const handleSort = () => {
251256
const column = th.getAttribute('data-sort');
252257
if (!column) return;
253258

@@ -271,6 +276,14 @@ PulseApp.ui.common = (() => {
271276
}
272277

273278
updateSortUI(tableId, th);
279+
};
280+
281+
th.addEventListener('click', handleSort);
282+
th.addEventListener('keydown', (e) => {
283+
if (e.key === 'Enter' || e.key === ' ') {
284+
e.preventDefault();
285+
handleSort();
286+
}
274287
});
275288
});
276289
}
@@ -297,7 +310,7 @@ PulseApp.ui.common = (() => {
297310
}
298311

299312
function generateNodeGroupHeaderCellHTML(text, colspan, cellTag = 'td') {
300-
const cellClasses = 'py-0.5 px-2 bg-gray-200 dark:bg-gray-700 subtle-stripes-light dark:subtle-stripes-dark text-left font-medium text-sm text-gray-700 dark:text-gray-300';
313+
const cellClasses = 'py-0.5 px-2 bg-gray-200 dark:bg-gray-700 subtle-stripes-light dark:subtle-stripes-dark text-left font-medium text-xs sm:text-sm text-gray-700 dark:text-gray-300';
301314
return `<${cellTag} colspan="${colspan}" class="${cellClasses}">${text}</${cellTag}>`;
302315
}
303316

0 commit comments

Comments
 (0)