refactor: Make frontend UI updates event-driven and consolidate storage data source

This commit is contained in:
courtmanr@gmail.com
2025-05-04 14:38:06 +01:00
parent 544c91c62d
commit ea860f05f1
4 changed files with 87 additions and 107 deletions
+36 -47
View File
@@ -1,12 +1,39 @@
document.addEventListener('DOMContentLoaded', function() {
const PulseApp = window.PulseApp || {};
function updateAllUITables() {
if (!PulseApp.state || !PulseApp.state.get('initialDataReceived')) {
return;
}
const nodesData = PulseApp.state.get('nodesData');
const pbsDataArray = PulseApp.state.get('pbsDataArray');
PulseApp.ui.nodes?.updateNodesTable(nodesData);
PulseApp.ui.dashboard?.updateDashboardTable();
PulseApp.ui.storage?.updateStorageInfo();
PulseApp.ui.pbs?.updatePbsInfo(pbsDataArray);
PulseApp.ui.backups?.updateBackupsTab();
const loadingOverlay = document.getElementById('loading-overlay');
if (loadingOverlay && loadingOverlay.style.display !== 'none') {
if (PulseApp.socketHandler?.isConnected()) {
console.log('[UI Update] First data received, hiding loading overlay.');
loadingOverlay.style.display = 'none';
} else {
console.log('[UI Update] Data received, but socket disconnected. Keeping loading overlay.');
}
}
PulseApp.thresholds?.logging?.checkThresholdViolations();
}
function initializeModules() {
PulseApp.state?.init?.(); // Although state is IIFE, init might be added later
PulseApp.config?.init?.(); // Although config is obj literal, init might be added later
PulseApp.utils?.init?.(); // Although utils is obj literal, init might be added later
PulseApp.state?.init?.();
PulseApp.config?.init?.();
PulseApp.utils?.init?.();
PulseApp.theme?.init?.();
PulseApp.socketHandler?.init?.();
PulseApp.socketHandler?.init?.(updateAllUITables);
PulseApp.tooltips?.init?.();
PulseApp.ui = PulseApp.ui || {};
@@ -14,7 +41,7 @@ document.addEventListener('DOMContentLoaded', function() {
PulseApp.ui.nodes?.init?.();
PulseApp.ui.dashboard?.init?.();
PulseApp.ui.storage?.init?.();
PulseApp.ui.pbs?.initPbsEventListeners?.(); // Specific init for PBS listeners
PulseApp.ui.pbs?.initPbsEventListeners?.();
PulseApp.ui.backups?.init?.();
PulseApp.ui.thresholds?.init?.();
PulseApp.ui.common?.init?.();
@@ -26,24 +53,19 @@ document.addEventListener('DOMContentLoaded', function() {
function validateCriticalElements() {
const criticalElements = [
'connection-status',
'main-table', // Check for table itself
// 'custom-tooltip', // Non-critical
// 'slider-value-tooltip', // Non-critical
'dashboard-search', // Optional but important
'dashboard-status-text', // Status display
'app-version' // Version display
'main-table',
'dashboard-search',
'dashboard-status-text',
'app-version'
];
let allFound = true;
criticalElements.forEach(id => {
if (!document.getElementById(id)) {
console.error(`Critical element #${id} not found!`);
// allFound = false; // Decide if you want to stop execution
}
});
// Check for table body specifically needed by dashboard updates
if (!document.querySelector('#main-table tbody')) {
console.error('Critical element #main-table tbody not found!');
// allFound = false;
}
return allFound;
}
@@ -65,44 +87,11 @@ document.addEventListener('DOMContentLoaded', function() {
});
}
function updateAllUITables() {
const nodesData = PulseApp.state.get('nodesData');
const pbsDataArray = PulseApp.state.get('pbsDataArray');
const storageData = PulseApp.state.get('storageData');
PulseApp.ui.nodes?.updateNodesTable(nodesData);
PulseApp.ui.dashboard?.updateDashboardTable(); // Refreshes data internally
PulseApp.ui.storage?.updateStorageInfo(); // Uses state internaly
PulseApp.ui.pbs?.updatePbsInfo(pbsDataArray);
PulseApp.ui.backups?.updateBackupsTab();
const loadingOverlay = document.getElementById('loading-overlay');
if (loadingOverlay && loadingOverlay.style.display !== 'none') {
if (PulseApp.socketHandler.isConnected()) {
console.log('[UI Update] Hiding loading overlay.');
loadingOverlay.style.display = 'none';
} else {
}
}
}
// --- Main Execution ---
if (!validateCriticalElements()) {
console.error("Stopping JS execution due to missing critical elements.");
// Optionally display a user-facing error message here
return;
}
initializeModules();
fetchVersion();
PulseApp.ui.storage.fetchStorageData(); // Initial fetch
setInterval(() => {
if (PulseApp.state.get('initialDataReceived')) {
updateAllUITables();
PulseApp.thresholds.logging?.checkThresholdViolations();
}
}, 2000); // UI update interval
setInterval(PulseApp.ui.storage.fetchStorageData, 30000); // Storage fetch interval
});
+36 -2
View File
@@ -1,11 +1,18 @@
PulseApp.socketHandler = (() => {
let socket = null;
function init() {
// Expose the UI update function reference
let updateAllUITablesRef = () => {
console.warn('[socketHandler] updateAllUITablesRef is not yet assigned.');
};
function init(updateFunctionRef) {
socket = io();
updateAllUITablesRef = updateFunctionRef; // Assign the function passed from main.js
socket.on('connect', handleConnect);
socket.on('disconnect', handleDisconnect);
socket.on('initialState', handleInitialState);
socket.on('rawData', handleRawData);
socket.on('pbsInitialStatus', handlePbsInitialStatus);
@@ -43,6 +50,26 @@ PulseApp.socketHandler = (() => {
}
}
function handleInitialState(state) {
console.log('[socketHandler] Received initial state:', state);
if (state && state.loading) {
// Update status text to indicate loading
const statusText = document.getElementById('dashboard-status-text');
if (statusText) {
statusText.textContent = 'Loading initial data...';
}
// Ensure loading overlay is visible
const loadingOverlay = document.getElementById('loading-overlay');
if (loadingOverlay && loadingOverlay.style.display === 'none') {
const loadingText = loadingOverlay.querySelector('p');
if (loadingText) {
loadingText.textContent = 'Loading data...'; // Or a specific initial loading message
}
loadingOverlay.style.display = 'flex';
}
}
}
function handleRawData(jsonData) {
try {
const data = typeof jsonData === 'string' ? JSON.parse(jsonData) : jsonData;
@@ -72,7 +99,14 @@ PulseApp.socketHandler = (() => {
PulseApp.state.set('initialDataReceived', true);
}
// --- Trigger UI update after processing data ---
if (typeof updateAllUITablesRef === 'function') {
updateAllUITablesRef();
} else {
console.error('[socketHandler] updateAllUITablesRef is not a function!');
}
// --- END Trigger ---
} catch (e) {
console.error('Error processing received rawData:', e, jsonData);
-1
View File
@@ -10,7 +10,6 @@ PulseApp.state = (() => {
metricsData: [],
dashboardData: [],
pbsDataArray: [],
storageData: {},
dashboardHistory: {},
initialDataReceived: false,
isThresholdRowVisible: false,
+15 -57
View File
@@ -2,39 +2,6 @@ PulseApp.ui = PulseApp.ui || {};
PulseApp.ui.storage = (() => {
async function fetchStorageData() {
try {
const response = await fetch('/api/storage');
if (!response.ok) {
let serverErrorMsg = `Server responded with status: ${response.status} ${response.statusText}`;
try {
const errorJson = await response.json();
if (errorJson && errorJson.globalError) {
serverErrorMsg = errorJson.globalError;
} else if (errorJson) {
serverErrorMsg += ` | Body: ${JSON.stringify(errorJson)}`;
}
} catch (parseError) {
}
throw new Error(serverErrorMsg);
}
const fetchedData = await response.json();
PulseApp.state.set('storageData', fetchedData);
} catch (error) {
let finalErrorMessage = 'Failed to load storage data due to an unknown error.';
if (error instanceof TypeError) {
finalErrorMessage = `Failed to load storage data due to a network error: ${error.message}`;
console.error('Network error during storage fetch:', error);
} else {
finalErrorMessage = error.message;
console.error('Error processing storage response:', error);
}
console.error(`Storage fetch failed, preserving previous data. Error: ${finalErrorMessage}`);
}
}
function getStorageTypeIcon(type) {
switch(type) {
case 'dir':
@@ -108,25 +75,24 @@ PulseApp.ui.storage = (() => {
contentDiv.innerHTML = '';
contentDiv.className = '';
const storage = PulseApp.state.get('storageData');
const nodes = PulseApp.state.get('nodesData') || [];
if (storage && storage.globalError) {
contentDiv.innerHTML = `<p class="p-4 text-red-700 dark:text-red-300">Error: ${storage.globalError}</p>`;
if (!Array.isArray(nodes) || nodes.length === 0) {
contentDiv.innerHTML = '<p class="text-gray-500 dark:text-gray-400 p-4 text-center">No node or storage data available.</p>';
return;
}
const nodeKeys = storage ? Object.keys(storage) : [];
const hasValidNodeData = nodeKeys.length > 0 && nodeKeys.some(key => Array.isArray(storage[key]));
const allNodesAreErrors = nodeKeys.length > 0 && nodeKeys.every(key => storage[key] && storage[key].error);
const storageByNode = nodes.reduce((acc, node) => {
if (node && node.node) {
acc[node.node] = Array.isArray(node.storage) ? node.storage : [];
}
return acc;
}, {});
const nodeKeys = Object.keys(storageByNode);
if (nodeKeys.length === 0) {
contentDiv.innerHTML = '<p class="text-gray-500 dark:text-gray-400 p-4 text-center">No storage data received from server.</p>';
return;
} else if (allNodesAreErrors) {
contentDiv.innerHTML = '<p class="text-red-600 dark:text-red-400 p-4 text-center">Failed to load storage data for all nodes. Check server logs.</p>';
return;
} else if (!hasValidNodeData) {
contentDiv.innerHTML = '<p class="text-yellow-600 dark:text-yellow-400 p-4 text-center">Received unexpected storage data format from server.</p>';
contentDiv.innerHTML = '<p class="text-gray-500 dark:text-gray-400 p-4 text-center">No storage data found associated with nodes.</p>';
return;
}
@@ -150,10 +116,10 @@ PulseApp.ui.storage = (() => {
const tbody = document.createElement('tbody');
tbody.className = 'divide-y divide-gray-200 dark:divide-gray-600';
const sortedNodeNames = Object.keys(storage).sort((a, b) => a.localeCompare(b));
const sortedNodeNames = Object.keys(storageByNode).sort((a, b) => a.localeCompare(b));
sortedNodeNames.forEach(nodeName => {
const nodeStorageData = storage[nodeName];
const nodeStorageData = storageByNode[nodeName];
const nodeHeaderRow = document.createElement('tr');
nodeHeaderRow.className = 'bg-gray-100 dark:bg-gray-700/80 font-semibold text-gray-700 dark:text-gray-300 text-xs node-storage-header';
@@ -164,14 +130,7 @@ PulseApp.ui.storage = (() => {
</td>`;
tbody.appendChild(nodeHeaderRow);
if (nodeStorageData.error) {
const errorRow = document.createElement('tr');
errorRow.innerHTML = `<td colspan="7" class="p-2 px-3 text-sm text-red-600 dark:text-red-400 italic">Error loading storage: ${nodeStorageData.error}</td>`;
tbody.appendChild(errorRow);
return;
}
if (!Array.isArray(nodeStorageData) || nodeStorageData.length === 0) {
if (nodeStorageData.length === 0) {
const noDataRow = document.createElement('tr');
noDataRow.innerHTML = `<td colspan="7" class="p-2 px-3 text-sm text-gray-500 dark:text-gray-400 italic">No storage configured or found for this node.</td>`;
tbody.appendChild(noDataRow);
@@ -221,7 +180,6 @@ PulseApp.ui.storage = (() => {
}
return {
fetchStorageData,
updateStorageInfo
};
})();