mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 18:45:53 +00:00
fix: improve reliability and user experience across components
- Update PBS connectivity check endpoint for better compatibility - Make alert checking asynchronous to prevent blocking - Update test expectations for webhook error messages with retry counts - Enhance alerts handler with better event listeners and error handling - Improve PBS UI by replacing symbols with clearer text labels 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1422,7 +1422,7 @@ async function fetchPbsData(currentPbsApiClients) {
|
||||
// Quick connectivity check for PBS to fail fast
|
||||
try {
|
||||
await Promise.race([
|
||||
pbsClient.client.get('/api2/json/version'),
|
||||
pbsClient.client.get('/version'),
|
||||
new Promise((_, reject) =>
|
||||
setTimeout(() => reject(new Error('PBS connectivity check timeout')), 3000)
|
||||
)
|
||||
|
||||
+2
-2
@@ -313,10 +313,10 @@ function addPerformanceSnapshot(type, duration, errorCount) {
|
||||
}
|
||||
}
|
||||
|
||||
function checkAlertsForMetrics() {
|
||||
async function checkAlertsForMetrics() {
|
||||
try {
|
||||
const allGuests = [...state.vms, ...state.containers];
|
||||
alertManager.checkMetrics(allGuests, state.metrics);
|
||||
await alertManager.checkMetrics(allGuests, state.metrics);
|
||||
} catch (error) {
|
||||
console.error('[State Manager] Error checking alerts:', error);
|
||||
}
|
||||
|
||||
@@ -225,7 +225,7 @@ describe('AlertManager Webhook Functionality', () => {
|
||||
|
||||
await expect(
|
||||
alertManager.sendWebhookNotification(mockWebhookChannel, mockAlert)
|
||||
).rejects.toThrow('Webhook failed: 404 Not Found');
|
||||
).rejects.toThrow('Webhook failed after 3 attempts: 404 Not Found');
|
||||
});
|
||||
|
||||
test('should handle network errors gracefully', async () => {
|
||||
@@ -235,7 +235,7 @@ describe('AlertManager Webhook Functionality', () => {
|
||||
|
||||
await expect(
|
||||
alertManager.sendWebhookNotification(mockWebhookChannel, mockAlert)
|
||||
).rejects.toThrow(`Webhook failed: No response from ${mockWebhookChannel.config.url}`);
|
||||
).rejects.toThrow(`Webhook failed after 3 attempts: No response from ${mockWebhookChannel.config.url}`);
|
||||
});
|
||||
|
||||
test('should handle other errors gracefully', async () => {
|
||||
@@ -244,7 +244,7 @@ describe('AlertManager Webhook Functionality', () => {
|
||||
|
||||
await expect(
|
||||
alertManager.sendWebhookNotification(mockWebhookChannel, mockAlert)
|
||||
).rejects.toThrow(`Webhook failed: ${errorMessage}`);
|
||||
).rejects.toThrow(`Webhook failed after 3 attempts: ${errorMessage}`);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -142,11 +142,29 @@ PulseApp.alerts = (() => {
|
||||
}
|
||||
|
||||
function setupEventListeners() {
|
||||
let socketListenersSetup = false;
|
||||
|
||||
// Wait for socket to be available and set up event listeners
|
||||
const setupSocketListeners = () => {
|
||||
if (window.socket && window.socket.connected) {
|
||||
if (window.socket && !socketListenersSetup) {
|
||||
// Set up alert event listeners
|
||||
window.socket.on('alert', handleNewAlert);
|
||||
window.socket.on('alertResolved', handleResolvedAlert);
|
||||
window.socket.on('alertEscalated', handleEscalatedAlert);
|
||||
window.socket.on('alertAcknowledged', handleAcknowledgedAlert);
|
||||
|
||||
// Handle reconnection - reload alert data when reconnected
|
||||
window.socket.on('connect', () => {
|
||||
console.log('[Alerts] Socket reconnected, reloading alert data');
|
||||
loadInitialData();
|
||||
});
|
||||
|
||||
window.socket.on('disconnect', () => {
|
||||
console.log('[Alerts] Socket disconnected');
|
||||
});
|
||||
|
||||
socketListenersSetup = true;
|
||||
console.log('[Alerts] Socket event listeners configured');
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -250,7 +268,7 @@ PulseApp.alerts = (() => {
|
||||
${SEVERITY_ICONS.info}
|
||||
</svg>
|
||||
<p class="text-xs mb-3">No active alerts</p>
|
||||
<button onclick="PulseApp.alerts.hideAlertsDropdown(); PulseApp.ui.alertManagementModal.openModal();"
|
||||
<button onclick="PulseApp.alerts.hideAlertsDropdown(); if (PulseApp.ui && PulseApp.ui.alertManagementModal) { PulseApp.ui.alertManagementModal.openModal(); } else { console.error('Alert management modal not available'); }"
|
||||
class="w-full px-3 py-2 bg-blue-500 hover:bg-blue-600 text-white text-xs font-medium rounded transition-colors">
|
||||
Manage Alerts
|
||||
</button>
|
||||
@@ -321,7 +339,7 @@ PulseApp.alerts = (() => {
|
||||
// Add Manage Alerts button to the bottom
|
||||
content += `
|
||||
<div class="border-t border-gray-200 dark:border-gray-700 p-2">
|
||||
<button onclick="PulseApp.alerts.hideAlertsDropdown(); PulseApp.ui.alertManagementModal.openModal();"
|
||||
<button onclick="PulseApp.alerts.hideAlertsDropdown(); if (PulseApp.ui && PulseApp.ui.alertManagementModal) { PulseApp.ui.alertManagementModal.openModal(); } else { console.error('Alert management modal not available'); }"
|
||||
class="w-full px-3 py-2 bg-blue-500 hover:bg-blue-600 text-white text-xs font-medium rounded transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3 inline mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
|
||||
@@ -414,7 +432,8 @@ PulseApp.alerts = (() => {
|
||||
<div class="flex-shrink-0 space-x-1">
|
||||
${!acknowledged ? `
|
||||
<button onclick="PulseApp.alerts.acknowledgeAlert('${alert.id}', '${alert.ruleId}');"
|
||||
class="text-xs px-1 py-0.5 bg-green-500 text-white rounded hover:bg-green-600 focus:outline-none">
|
||||
class="text-xs px-1 py-0.5 bg-green-500 text-white rounded hover:bg-green-600 focus:outline-none"
|
||||
title="Acknowledge alert">
|
||||
✓
|
||||
</button>
|
||||
` : ''}
|
||||
@@ -626,19 +645,30 @@ PulseApp.alerts = (() => {
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Alerts] Failed to acknowledge alert:', error);
|
||||
// Only show notification for actual errors
|
||||
// showNotification({ message: `Failed to acknowledge alert: ${error.message}` }, 'warning');
|
||||
// Show user feedback for acknowledgment failures
|
||||
showToastNotification(`Failed to acknowledge alert: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// Track cleanup timeouts to prevent memory leaks
|
||||
const cleanupTimeouts = new Map();
|
||||
|
||||
function scheduleAcknowledgedCleanup(alertId) {
|
||||
setTimeout(() => {
|
||||
// Clear any existing timeout for this alert
|
||||
if (cleanupTimeouts.has(alertId)) {
|
||||
clearTimeout(cleanupTimeouts.get(alertId));
|
||||
}
|
||||
|
||||
const timeoutId = setTimeout(() => {
|
||||
activeAlerts = activeAlerts.filter(a => a.id !== alertId);
|
||||
updateHeaderIndicator();
|
||||
if (alertDropdown && !alertDropdown.classList.contains('hidden')) {
|
||||
updateDropdownContent();
|
||||
}
|
||||
cleanupTimeouts.delete(alertId);
|
||||
}, ACKNOWLEDGED_CLEANUP_DELAY);
|
||||
|
||||
cleanupTimeouts.set(alertId, timeoutId);
|
||||
}
|
||||
|
||||
function toggleAcknowledgedSection() {
|
||||
@@ -689,7 +719,7 @@ PulseApp.alerts = (() => {
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Alerts] Failed to suppress alert:', error);
|
||||
// showNotification({ message: `Failed to suppress alert: ${error.message}` }, 'warning');
|
||||
showToastNotification(`Failed to suppress alert: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -771,6 +801,68 @@ PulseApp.alerts = (() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Additional socket event handlers
|
||||
function handleEscalatedAlert(alert) {
|
||||
console.log('[Alerts] Alert escalated:', alert);
|
||||
|
||||
// Update existing alert or add if not found
|
||||
const existingIndex = activeAlerts.findIndex(a => a.id === alert.id);
|
||||
if (existingIndex >= 0) {
|
||||
activeAlerts[existingIndex] = { ...activeAlerts[existingIndex], ...alert };
|
||||
} else {
|
||||
activeAlerts.unshift(alert);
|
||||
}
|
||||
|
||||
updateHeaderIndicator();
|
||||
if (alertDropdown && !alertDropdown.classList.contains('hidden')) {
|
||||
updateDropdownContent();
|
||||
}
|
||||
|
||||
// Show escalation notification
|
||||
showToastNotification(`Alert escalated: ${alert.rule.name} for ${alert.guest.name}`, 'critical');
|
||||
}
|
||||
|
||||
function handleAcknowledgedAlert(alert) {
|
||||
console.log('[Alerts] Alert acknowledged:', alert);
|
||||
|
||||
// Update existing alert
|
||||
const existingIndex = activeAlerts.findIndex(a => a.id === alert.id);
|
||||
if (existingIndex >= 0) {
|
||||
activeAlerts[existingIndex] = { ...activeAlerts[existingIndex], acknowledged: true, acknowledgedAt: Date.now() };
|
||||
updateHeaderIndicator();
|
||||
if (alertDropdown && !alertDropdown.classList.contains('hidden')) {
|
||||
updateDropdownContent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup function to prevent memory leaks
|
||||
function cleanup() {
|
||||
// Clear all cleanup timeouts
|
||||
for (const timeoutId of cleanupTimeouts.values()) {
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
cleanupTimeouts.clear();
|
||||
|
||||
// Remove socket listeners if needed
|
||||
if (window.socket) {
|
||||
window.socket.off('alert', handleNewAlert);
|
||||
window.socket.off('alertResolved', handleResolvedAlert);
|
||||
window.socket.off('alertEscalated', handleEscalatedAlert);
|
||||
window.socket.off('alertAcknowledged', handleAcknowledgedAlert);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function for toast notifications
|
||||
function showToastNotification(message, type = 'info') {
|
||||
if (window.PulseApp && window.PulseApp.ui && window.PulseApp.ui.toastNotifications) {
|
||||
window.PulseApp.ui.toastNotifications.show(message, type);
|
||||
} else {
|
||||
// Fallback to basic notification
|
||||
showNotification({ message }, type);
|
||||
}
|
||||
}
|
||||
|
||||
// Public API
|
||||
return {
|
||||
@@ -784,7 +876,8 @@ PulseApp.alerts = (() => {
|
||||
markAllAsAcknowledged,
|
||||
toggleAcknowledgedSection,
|
||||
getActiveAlerts: () => activeAlerts,
|
||||
getAlertHistory: () => alertHistory
|
||||
getAlertHistory: () => alertHistory,
|
||||
cleanup
|
||||
};
|
||||
})();
|
||||
|
||||
|
||||
+23
-27
@@ -179,11 +179,11 @@ PulseApp.ui.pbs = (() => {
|
||||
|
||||
const getPbsStatusIcon = (status) => {
|
||||
if (status === 'OK') {
|
||||
return `<span class="${CSS_CLASSES.TEXT_GREEN_500_DARK_GREEN_400}" title="OK">✓</span>`;
|
||||
return `<span class="${CSS_CLASSES.TEXT_GREEN_500_DARK_GREEN_400}" title="OK">OK</span>`;
|
||||
} else if (status === 'running') {
|
||||
return `<span class="${CSS_CLASSES.INLINE_BLOCK} ${CSS_CLASSES.ANIMATE_SPIN} ${CSS_CLASSES.ROUNDED_FULL} ${CSS_CLASSES.H_3} ${CSS_CLASSES.W_3} ${CSS_CLASSES.BORDER_T_2} ${CSS_CLASSES.BORDER_B_2} ${CSS_CLASSES.BORDER_BLUE_500}" title="Running"></span>`;
|
||||
} else if (status) {
|
||||
return `<span class="${CSS_CLASSES.TEXT_RED_500_DARK_RED_400} ${CSS_CLASSES.FONT_BOLD}" title="${status}">✗</span>`;
|
||||
return `<span class="${CSS_CLASSES.TEXT_RED_500_DARK_RED_400} ${CSS_CLASSES.FONT_BOLD}" title="${status}">ERROR</span>`;
|
||||
} else {
|
||||
return `<span class="${CSS_CLASSES.TEXT_GRAY_400}" title="Unknown">?</span>`;
|
||||
}
|
||||
@@ -191,13 +191,13 @@ PulseApp.ui.pbs = (() => {
|
||||
|
||||
const getPbsStatusDisplay = (status) => {
|
||||
if (status === 'OK') {
|
||||
return `<span class="${CSS_CLASSES.TEXT_GREEN_500_DARK_GREEN_400}">✓ OK</span>`;
|
||||
return `<span class="${CSS_CLASSES.TEXT_GREEN_500_DARK_GREEN_400}">OK</span>`;
|
||||
} else if (status === 'running') {
|
||||
return `<span class="${CSS_CLASSES.INLINE_BLOCK} ${CSS_CLASSES.ANIMATE_SPIN} ${CSS_CLASSES.ROUNDED_FULL} ${CSS_CLASSES.H_3} ${CSS_CLASSES.W_3} ${CSS_CLASSES.BORDER_T_2} ${CSS_CLASSES.BORDER_B_2} ${CSS_CLASSES.BORDER_BLUE_500}" title="Running"></span> <span class="${CSS_CLASSES.TEXT_BLUE_600_DARK_BLUE_400}">Running</span>`;
|
||||
} else if (status) {
|
||||
// For failed tasks, show the full error message
|
||||
const shortStatus = status.length > 50 ? `${status.substring(0, 47)}...` : status;
|
||||
return `<span class="${CSS_CLASSES.TEXT_RED_500_DARK_RED_400} ${CSS_CLASSES.FONT_BOLD}" title="${status}">✗</span> <span class="${CSS_CLASSES.TEXT_RED_600_DARK_RED_400} ${CSS_CLASSES.TEXT_XS}" title="${status}">${shortStatus}</span>`;
|
||||
return `<span class="${CSS_CLASSES.TEXT_RED_500_DARK_RED_400} ${CSS_CLASSES.FONT_BOLD}" title="${status}">ERROR</span> <span class="${CSS_CLASSES.TEXT_RED_600_DARK_RED_400} ${CSS_CLASSES.TEXT_XS}" title="${status}">${shortStatus}</span>`;
|
||||
} else {
|
||||
return `<span class="${CSS_CLASSES.TEXT_GRAY_400}">? Unknown</span>`;
|
||||
}
|
||||
@@ -288,7 +288,7 @@ PulseApp.ui.pbs = (() => {
|
||||
const targetElement = document.createElement('div');
|
||||
targetElement.className = 'font-medium text-sm truncate pr-2 flex-1';
|
||||
if (isFailed) {
|
||||
targetElement.innerHTML = `<span class="text-xs text-gray-400 mr-1">▶</span>${target}`;
|
||||
targetElement.innerHTML = `${target}`;
|
||||
} else {
|
||||
targetElement.textContent = target;
|
||||
}
|
||||
@@ -331,7 +331,7 @@ PulseApp.ui.pbs = (() => {
|
||||
if (isFailed) {
|
||||
const expandButton = document.createElement('button');
|
||||
expandButton.className = 'mt-3 w-full py-2 px-3 text-xs text-blue-600 dark:text-blue-400 border border-blue-200 dark:border-blue-600 rounded bg-blue-50 dark:bg-blue-900/20 hover:bg-blue-100 dark:hover:bg-blue-900/30 transition-colors tap-target';
|
||||
expandButton.textContent = 'Show Error Details ▼';
|
||||
expandButton.textContent = 'Show Error Details';
|
||||
|
||||
expandButton.addEventListener('click', (event) => {
|
||||
event.stopPropagation();
|
||||
@@ -343,15 +343,15 @@ PulseApp.ui.pbs = (() => {
|
||||
if (existingDetailCard && existingDetailCard.classList.contains('mobile-task-detail-card')) {
|
||||
// Toggle existing detail card - collapse
|
||||
existingDetailCard.remove();
|
||||
expandButton.textContent = 'Show Error Details ▼';
|
||||
targetElement.innerHTML = `<span class="text-xs text-gray-400 mr-1">▶</span>${target}`;
|
||||
expandButton.textContent = 'Show Error Details';
|
||||
targetElement.innerHTML = `${target}`;
|
||||
expandedTaskState.delete(upid);
|
||||
} else {
|
||||
// Create and show detail card - expand
|
||||
const detailCard = _createMobileTaskDetailCard(task);
|
||||
card.insertAdjacentElement('afterend', detailCard);
|
||||
expandButton.textContent = 'Hide Error Details ▲';
|
||||
targetElement.innerHTML = `<span class="text-xs text-gray-400 mr-1">▼</span>${target}`;
|
||||
expandButton.textContent = 'Hide Error Details';
|
||||
targetElement.innerHTML = `${target}`;
|
||||
expandedTaskState.add(upid);
|
||||
}
|
||||
});
|
||||
@@ -449,7 +449,7 @@ PulseApp.ui.pbs = (() => {
|
||||
|
||||
// Add expand indicator for failed tasks
|
||||
if (isFailed) {
|
||||
targetCell.innerHTML = `<span class="text-xs text-gray-400 mr-1">▶</span>${target}`;
|
||||
targetCell.innerHTML = `${target}`;
|
||||
} else {
|
||||
targetCell.textContent = target;
|
||||
}
|
||||
@@ -488,13 +488,13 @@ PulseApp.ui.pbs = (() => {
|
||||
if (existingDetailRow && existingDetailRow.classList.contains('task-detail-row')) {
|
||||
// Toggle existing detail row - collapse
|
||||
existingDetailRow.remove();
|
||||
targetCell.innerHTML = `<span class="text-xs text-gray-400 mr-1">▶</span>${target}`;
|
||||
targetCell.innerHTML = `${target}`;
|
||||
expandedTaskState.delete(upid); // Remove from global state
|
||||
} else {
|
||||
// Create and show detail row - expand
|
||||
const detailRow = _createTaskDetailRow(task);
|
||||
row.insertAdjacentElement('afterend', detailRow);
|
||||
targetCell.innerHTML = `<span class="text-xs text-gray-400 mr-1">▼</span>${target}`;
|
||||
targetCell.innerHTML = `${target}`;
|
||||
expandedTaskState.add(upid); // Add to global state
|
||||
}
|
||||
});
|
||||
@@ -658,7 +658,7 @@ PulseApp.ui.pbs = (() => {
|
||||
const target = parsePbsTaskTarget(task);
|
||||
const detailRow = _createTaskDetailRow(task);
|
||||
taskRow.insertAdjacentElement('afterend', detailRow);
|
||||
targetCell.innerHTML = `<span class="text-xs text-gray-400 mr-1">▼</span>${target}`;
|
||||
targetCell.innerHTML = `${target}`;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -739,9 +739,9 @@ PulseApp.ui.pbs = (() => {
|
||||
|
||||
let nameContent = ds.name || 'N/A';
|
||||
if (usagePercent >= 95) {
|
||||
nameElement.innerHTML = `<span class="text-red-700 dark:text-red-300">⚠ ${nameContent}</span><div class="text-xs text-red-600 dark:text-red-400 font-normal mt-1">CRITICAL: ${usagePercent}% full</div>`;
|
||||
nameElement.innerHTML = `<span class="text-red-700 dark:text-red-300">${nameContent}</span><div class="text-xs text-red-600 dark:text-red-400 font-normal mt-1">CRITICAL: ${usagePercent}% full</div>`;
|
||||
} else if (usagePercent >= 85) {
|
||||
nameElement.innerHTML = `<span class="text-yellow-700 dark:text-yellow-300">⚠ ${nameContent}</span><div class="text-xs text-yellow-600 dark:text-yellow-400 font-normal mt-1">WARNING: ${usagePercent}% full</div>`;
|
||||
nameElement.innerHTML = `<span class="text-yellow-700 dark:text-yellow-300">${nameContent}</span><div class="text-xs text-yellow-600 dark:text-yellow-400 font-normal mt-1">WARNING: ${usagePercent}% full</div>`;
|
||||
} else {
|
||||
nameElement.textContent = nameContent;
|
||||
}
|
||||
@@ -913,10 +913,10 @@ PulseApp.ui.pbs = (() => {
|
||||
// Add usage alert to name if critical
|
||||
let nameContent = ds.name || 'N/A';
|
||||
if (usagePercent >= 95) {
|
||||
nameContent = `⚠ ${nameContent} [CRITICAL: ${usagePercent}% full]`;
|
||||
nameContent = `${nameContent} [CRITICAL: ${usagePercent}% full]`;
|
||||
createCell(nameContent, ['text-red-700', 'dark:text-red-300', 'font-semibold', 'sticky', 'left-0', 'bg-white', 'dark:bg-gray-800', 'z-10']);
|
||||
} else if (usagePercent >= 85) {
|
||||
nameContent = `⚠ ${nameContent} [WARNING: ${usagePercent}% full]`;
|
||||
nameContent = `${nameContent} [WARNING: ${usagePercent}% full]`;
|
||||
createCell(nameContent, ['text-yellow-700', 'dark:text-yellow-300', 'font-semibold', 'sticky', 'left-0', 'bg-white', 'dark:bg-gray-800', 'z-10']);
|
||||
} else {
|
||||
createCell(nameContent, ['sticky', 'left-0', 'bg-white', 'dark:bg-gray-800', 'z-10']);
|
||||
@@ -1124,12 +1124,12 @@ PulseApp.ui.pbs = (() => {
|
||||
// More descriptive status text
|
||||
let statusHtml = '';
|
||||
if (failed > 0) {
|
||||
statusHtml = `<span class="${CSS_CLASSES.TEXT_RED_600_DARK_RED_400} ${CSS_CLASSES.FONT_BOLD}">⚠ ${failed} FAILED</span>`;
|
||||
statusHtml = `<span class="${CSS_CLASSES.TEXT_RED_600_DARK_RED_400} ${CSS_CLASSES.FONT_BOLD}">${failed} FAILED</span>`;
|
||||
if (ok > 0) {
|
||||
statusHtml += ` / <span class="${CSS_CLASSES.TEXT_GREEN_600_DARK_GREEN_400}">${ok} OK</span>`;
|
||||
}
|
||||
} else if (ok > 0) {
|
||||
statusHtml = `<span class="${CSS_CLASSES.TEXT_GREEN_600_DARK_GREEN_400}">✓ All OK (${ok})</span>`;
|
||||
statusHtml = `<span class="${CSS_CLASSES.TEXT_GREEN_600_DARK_GREEN_400}">All OK (${ok})</span>`;
|
||||
} else {
|
||||
statusHtml = `<span class="${CSS_CLASSES.TEXT_GRAY_600_DARK_GRAY_400}">No recent tasks</span>`;
|
||||
}
|
||||
@@ -1160,7 +1160,7 @@ PulseApp.ui.pbs = (() => {
|
||||
|
||||
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.innerHTML = `Recent ${title} Tasks <span id="${tableId}-status" class="text-xs font-normal text-gray-500"></span><span id="${tableId}-priority" class="text-xs text-red-600 dark:text-red-400 ml-2 hidden">⚠ Failed tasks shown first</span>`;
|
||||
heading.innerHTML = `Recent ${title} Tasks <span id="${tableId}-status" class="text-xs font-normal text-gray-500"></span><span id="${tableId}-priority" class="text-xs text-red-600 dark:text-red-400 ml-2 hidden">Failed tasks shown first</span>`;
|
||||
fragment.appendChild(heading);
|
||||
|
||||
const tableContainer = document.createElement('div');
|
||||
@@ -1434,7 +1434,7 @@ PulseApp.ui.pbs = (() => {
|
||||
</div>
|
||||
<div class="summary-card p-3 bg-gray-50 dark:bg-gray-700/50 rounded-lg text-center border border-gray-200 dark:border-gray-600">
|
||||
<div class="text-lg font-semibold ${isServerHealthy ? 'text-green-600 dark:text-green-400' : 'text-red-600 dark:text-red-400'}">
|
||||
${isServerHealthy ? '✓' : '✗'}
|
||||
${isServerHealthy ? 'OK' : 'ERROR'}
|
||||
</div>
|
||||
<div class="text-xs text-gray-600 dark:text-gray-400">Server</div>
|
||||
<div class="text-xs ${isServerHealthy ? 'text-green-600 dark:text-green-400' : 'text-red-600 dark:text-red-400'}">${isServerHealthy ? 'Online' : 'Error'}</div>
|
||||
@@ -1482,14 +1482,11 @@ PulseApp.ui.pbs = (() => {
|
||||
const overallHealthAndTitle = _calculateOverallHealth(pbsInstance);
|
||||
const statusInfo = _getInstanceStatusInfo(pbsInstance);
|
||||
|
||||
let statusIcon = '✓';
|
||||
let statusClass = 'text-green-600 dark:text-green-400';
|
||||
|
||||
if (pbsInstance.status === 'error') {
|
||||
statusIcon = '✗';
|
||||
statusClass = 'text-red-600 dark:text-red-400';
|
||||
} else if (pbsInstance.status !== 'ok') {
|
||||
statusIcon = '⚪';
|
||||
statusClass = 'text-yellow-600 dark:text-yellow-400';
|
||||
}
|
||||
|
||||
@@ -1503,7 +1500,6 @@ PulseApp.ui.pbs = (() => {
|
||||
|
||||
headerContent.innerHTML = `
|
||||
<div class="flex items-center gap-2 mb-1">
|
||||
<span class="${statusClass} text-sm">${statusIcon}</span>
|
||||
${instanceNameHtml}
|
||||
</div>
|
||||
<div class="text-xs text-gray-500 dark:text-gray-400 truncate">${statusInfo.statusText}</div>
|
||||
@@ -1511,7 +1507,7 @@ PulseApp.ui.pbs = (() => {
|
||||
|
||||
const chevron = document.createElement('span');
|
||||
chevron.className = 'text-gray-400 transition-transform duration-200';
|
||||
chevron.innerHTML = '▼';
|
||||
chevron.innerHTML = '';
|
||||
|
||||
header.appendChild(headerContent);
|
||||
header.appendChild(chevron);
|
||||
|
||||
Reference in New Issue
Block a user