fix: improve version API error handling and fix notification toggle listeners

- Add robust error handling to version API endpoint with fallback responses
- Fix duplicate event listeners on notification toggles by cloning elements
- Update toggle configuration keys to GLOBAL_EMAIL_ENABLED/GLOBAL_WEBHOOK_ENABLED
- Add debug toggles for troubleshooting notification components
- Remove visual feedback animations to prevent UI flashing

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
rcourtman
2025-06-12 10:19:17 +01:00
parent 8254a41604
commit 357bf6fc60
4 changed files with 236 additions and 43 deletions
+26 -6
View File
@@ -734,17 +734,37 @@ app.get('/api/version', async (req, res) => {
const packageJson = require('../package.json');
const currentVersion = packageJson.version || 'N/A';
// Use UpdateManager to check for updates respecting user's channel preference
const updateInfo = await updateManager.checkForUpdates();
let latestVersion = currentVersion;
let updateAvailable = false;
try {
// Try to check for updates, but don't fail if it doesn't work
const updateInfo = await updateManager.checkForUpdates();
latestVersion = updateInfo.latestVersion || currentVersion;
updateAvailable = updateInfo.hasUpdate || false;
} catch (updateError) {
// Log the error but continue with current version info
console.error("[Version API] Error checking for updates:", updateError.message);
}
res.json({
version: currentVersion,
latestVersion: updateInfo.latestVersion,
updateAvailable: updateInfo.hasUpdate
latestVersion: latestVersion,
updateAvailable: updateAvailable
});
} catch (error) {
console.error("Error in version endpoint:", error);
res.status(500).json({ error: "Could not retrieve version" });
console.error("[Version API] Error in version endpoint:", error);
// Still try to return current version if possible
try {
const packageJson = require('../package.json');
res.json({
version: packageJson.version || 'N/A',
latestVersion: packageJson.version || 'N/A',
updateAvailable: false
});
} catch (fallbackError) {
res.status(500).json({ error: "Could not retrieve version" });
}
}
});
+93
View File
@@ -0,0 +1,93 @@
// Debug function for notification toggles
// Copy and paste this into the browser console when the alert modal is open
function debugToggles() {
console.log('=== Toggle Debug Info ===');
// Check if modal is open
const modal = document.getElementById('alert-management-modal');
console.log('Modal found:', !!modal);
console.log('Modal visible:', modal && !modal.classList.contains('hidden'));
// Check active tab
const activeTab = document.querySelector('.alert-tab.active');
console.log('Active tab:', activeTab ? activeTab.getAttribute('data-tab') : 'none');
// Check toggle elements
const emailToggle = document.getElementById('global-email-toggle');
const webhookToggle = document.getElementById('global-webhook-toggle');
console.log('\n--- Toggle Elements ---');
console.log('Email toggle found:', !!emailToggle);
if (emailToggle) {
console.log('Email toggle checked:', emailToggle.checked);
console.log('Email toggle disabled:', emailToggle.disabled);
console.log('Email toggle parent:', emailToggle.parentElement);
}
console.log('\nWebhook toggle found:', !!webhookToggle);
if (webhookToggle) {
console.log('Webhook toggle checked:', webhookToggle.checked);
console.log('Webhook toggle disabled:', webhookToggle.disabled);
console.log('Webhook toggle parent:', webhookToggle.parentElement);
}
// Check config sections
const emailSection = document.getElementById('email-config-section');
const webhookSection = document.getElementById('webhook-config-section');
console.log('\n--- Config Sections ---');
console.log('Email section found:', !!emailSection);
if (emailSection) {
console.log('Email section opacity:', emailSection.style.opacity);
console.log('Email section inputs:', emailSection.querySelectorAll('input').length);
}
console.log('\nWebhook section found:', !!webhookSection);
if (webhookSection) {
console.log('Webhook section opacity:', webhookSection.style.opacity);
console.log('Webhook section inputs:', webhookSection.querySelectorAll('input').length);
}
// Check current config
console.log('\n--- Current Config ---');
if (window.PulseApp && window.PulseApp.ui && window.PulseApp.ui.alertManagementModal) {
console.log('PulseApp.ui.alertManagementModal available');
}
// Test toggle click
console.log('\n--- Testing Toggle Click ---');
if (emailToggle) {
console.log('Simulating email toggle click...');
const event = new Event('change', { bubbles: true });
emailToggle.checked = !emailToggle.checked;
emailToggle.dispatchEvent(event);
}
}
// Run the debug function
debugToggles();
// Additional helper to manually trigger toggle setup
function manualSetupToggles() {
if (window.PulseApp && window.PulseApp.ui && window.PulseApp.ui.alertManagementModal) {
// Try to access the setupNotificationToggles function if it's exposed
console.log('Attempting manual toggle setup...');
// Manually recreate the setup logic
const emailToggle = document.getElementById('global-email-toggle');
const webhookToggle = document.getElementById('global-webhook-toggle');
if (emailToggle) {
emailToggle.addEventListener('change', (e) => {
console.log('[Manual] Email toggle changed to:', e.target.checked);
});
}
if (webhookToggle) {
webhookToggle.addEventListener('change', (e) => {
console.log('[Manual] Webhook toggle changed to:', e.target.checked);
});
}
}
}
+62 -37
View File
@@ -165,7 +165,13 @@ PulseApp.ui.alertManagementModal = (() => {
openAlertRuleModal,
editAlertRule,
toggleAlertRule,
deleteAlertRule
deleteAlertRule,
// Debug functions
debugToggles: () => {
console.log('Email toggle:', document.getElementById('global-email-toggle'));
console.log('Webhook toggle:', document.getElementById('global-webhook-toggle'));
setupNotificationToggles();
}
};
Object.entries(functions).forEach(([name, func]) => {
@@ -574,20 +580,20 @@ PulseApp.ui.alertManagementModal = (() => {
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">Master switches for all alert notifications</p>
</div>
<div class="flex flex-wrap items-center gap-6">
<label class="flex items-center gap-3">
<div class="flex items-center gap-3">
<span class="text-sm text-gray-700 dark:text-gray-300">Email Notifications</span>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" id="global-email-toggle" class="sr-only peer">
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
<label for="global-email-toggle" class="relative inline-flex items-center cursor-pointer focus:outline-none">
<input type="checkbox" id="global-email-toggle" class="sr-only peer focus:outline-none">
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600 focus:outline-none"></div>
</label>
</label>
<label class="flex items-center gap-3">
</div>
<div class="flex items-center gap-3">
<span class="text-sm text-gray-700 dark:text-gray-300">Webhook Notifications</span>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" id="global-webhook-toggle" class="sr-only peer">
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
<label for="global-webhook-toggle" class="relative inline-flex items-center cursor-pointer focus:outline-none">
<input type="checkbox" id="global-webhook-toggle" class="sr-only peer focus:outline-none">
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600 focus:outline-none"></div>
</label>
</label>
</div>
</div>
</div>
</div>
@@ -721,8 +727,10 @@ PulseApp.ui.alertManagementModal = (() => {
// Load all alert rules (unified)
loadAllAlertRules();
// Set up event listeners for notifications
setupNotificationToggles();
// Set up event listeners for notifications with a small delay to ensure DOM is ready
setTimeout(() => {
setupNotificationToggles();
}, 10);
// setupEmailTestButton(); // Redundant - handled in initializeNotificationsTab
// Create rule button now uses onclick attribute directly
@@ -1276,8 +1284,13 @@ PulseApp.ui.alertManagementModal = (() => {
const emailToggle = document.getElementById('global-email-toggle');
const webhookToggle = document.getElementById('global-webhook-toggle');
if (emailToggle) {
emailToggle.addEventListener('change', async (e) => {
// Remove any existing listeners first
const newEmailToggle = emailToggle.cloneNode(true);
emailToggle.parentNode.replaceChild(newEmailToggle, emailToggle);
newEmailToggle.addEventListener('change', async (e) => {
try {
await handleGlobalEmailToggle(e.target.checked);
} catch (error) {
@@ -1287,7 +1300,11 @@ PulseApp.ui.alertManagementModal = (() => {
}
if (webhookToggle) {
webhookToggle.addEventListener('change', async (e) => {
// Remove any existing listeners first
const newWebhookToggle = webhookToggle.cloneNode(true);
webhookToggle.parentNode.replaceChild(newWebhookToggle, webhookToggle);
newWebhookToggle.addEventListener('change', async (e) => {
try {
await handleGlobalWebhookToggle(e.target.checked);
} catch (error) {
@@ -1302,7 +1319,7 @@ PulseApp.ui.alertManagementModal = (() => {
const response = await fetch('/api/config', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ALERT_EMAIL_ENABLED: enabled ? 'true' : 'false' })
body: JSON.stringify({ GLOBAL_EMAIL_ENABLED: enabled ? 'true' : 'false' })
});
if (!response.ok) {
@@ -1311,17 +1328,20 @@ PulseApp.ui.alertManagementModal = (() => {
// Update currentConfig
if (currentConfig) {
currentConfig.ALERT_EMAIL_ENABLED = enabled ? 'true' : 'false';
currentConfig.GLOBAL_EMAIL_ENABLED = enabled ? 'true' : 'false';
}
// Show visual feedback
const toggleEl = document.getElementById('global-email-toggle');
if (toggleEl) {
// Briefly flash the toggle to indicate success
const parent = toggleEl.parentElement;
parent.classList.add('ring-2', 'ring-green-500');
setTimeout(() => parent.classList.remove('ring-2', 'ring-green-500'), 1000);
}
// Update visibility of email configuration section
updateEmailConfigVisibility(enabled);
// Visual feedback disabled - remove this comment block to re-enable
// const toggleEl = document.getElementById('global-email-toggle');
// if (toggleEl) {
// // Briefly flash the toggle to indicate success
// const parent = toggleEl.parentElement;
// parent.classList.add('ring-2', 'ring-green-500');
// setTimeout(() => parent.classList.remove('ring-2', 'ring-green-500'), 1000);
// }
} catch (error) {
console.error('Error updating email toggle:', error);
@@ -1339,7 +1359,7 @@ PulseApp.ui.alertManagementModal = (() => {
const response = await fetch('/api/config', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ALERT_WEBHOOK_ENABLED: enabled ? 'true' : 'false' })
body: JSON.stringify({ GLOBAL_WEBHOOK_ENABLED: enabled ? 'true' : 'false' })
});
if (!response.ok) {
@@ -1348,17 +1368,20 @@ PulseApp.ui.alertManagementModal = (() => {
// Update currentConfig
if (currentConfig) {
currentConfig.ALERT_WEBHOOK_ENABLED = enabled ? 'true' : 'false';
currentConfig.GLOBAL_WEBHOOK_ENABLED = enabled ? 'true' : 'false';
}
// Show visual feedback
const toggleEl = document.getElementById('global-webhook-toggle');
if (toggleEl) {
// Briefly flash the toggle to indicate success
const parent = toggleEl.parentElement;
parent.classList.add('ring-2', 'ring-green-500');
setTimeout(() => parent.classList.remove('ring-2', 'ring-green-500'), 1000);
}
// Update visibility of webhook configuration section
updateWebhookConfigVisibility(enabled);
// Visual feedback disabled - remove this comment block to re-enable
// const toggleEl = document.getElementById('global-webhook-toggle');
// if (toggleEl) {
// // Briefly flash the toggle to indicate success
// const parent = toggleEl.parentElement;
// parent.classList.add('ring-2', 'ring-green-500');
// setTimeout(() => parent.classList.remove('ring-2', 'ring-green-500'), 1000);
// }
} catch (error) {
console.error('Error updating webhook toggle:', error);
@@ -1389,6 +1412,7 @@ PulseApp.ui.alertManagementModal = (() => {
inputs.forEach(input => {
input.disabled = !enabled;
});
} else {
}
}
@@ -1401,6 +1425,7 @@ PulseApp.ui.alertManagementModal = (() => {
inputs.forEach(input => {
input.disabled = !enabled;
});
} else {
}
}
@@ -1420,14 +1445,14 @@ PulseApp.ui.alertManagementModal = (() => {
// });
if (emailToggle && currentConfig) {
const emailEnabled = currentConfig.ALERT_EMAIL_ENABLED === 'true';
const emailEnabled = currentConfig.GLOBAL_EMAIL_ENABLED === 'true';
// console.log('[Global Toggles] Setting email toggle to:', emailEnabled);
emailToggle.checked = emailEnabled;
updateEmailConfigVisibility(emailEnabled);
}
if (webhookToggle && currentConfig) {
const webhookEnabled = currentConfig.ALERT_WEBHOOK_ENABLED === 'true';
const webhookEnabled = currentConfig.GLOBAL_WEBHOOK_ENABLED === 'true';
// console.log('[Global Toggles] Setting webhook toggle to:', webhookEnabled);
webhookToggle.checked = webhookEnabled;
updateWebhookConfigVisibility(webhookEnabled);
+55
View File
@@ -0,0 +1,55 @@
<!DOCTYPE html>
<html>
<head>
<title>Toggle Debug Test</title>
<script>
// Test function to debug toggle behavior
function debugToggles() {
console.log('=== Toggle Debug Info ===');
// Check for email toggle
const emailToggle = document.getElementById('global-email-toggle');
console.log('Email toggle found:', !!emailToggle);
if (emailToggle) {
console.log('Email toggle checked:', emailToggle.checked);
console.log('Email toggle disabled:', emailToggle.disabled);
}
// Check for webhook toggle
const webhookToggle = document.getElementById('global-webhook-toggle');
console.log('Webhook toggle found:', !!webhookToggle);
if (webhookToggle) {
console.log('Webhook toggle checked:', webhookToggle.checked);
console.log('Webhook toggle disabled:', webhookToggle.disabled);
}
// Check for config sections
const emailSection = document.getElementById('email-config-section');
console.log('Email config section found:', !!emailSection);
if (emailSection) {
console.log('Email section opacity:', emailSection.style.opacity);
}
const webhookSection = document.getElementById('webhook-config-section');
console.log('Webhook config section found:', !!webhookSection);
if (webhookSection) {
console.log('Webhook section opacity:', webhookSection.style.opacity);
}
// Check if functions exist
console.log('updateEmailConfigVisibility exists:', typeof updateEmailConfigVisibility === 'function');
console.log('updateWebhookConfigVisibility exists:', typeof updateWebhookConfigVisibility === 'function');
console.log('=== End Debug Info ===');
}
// Add this to the browser console
window.debugToggles = debugToggles;
</script>
</head>
<body>
<h1>Toggle Debug Test</h1>
<p>Open the browser console and run: <code>debugToggles()</code></p>
<p>Or add this to the alertManagementModal.js temporarily to debug</p>
</body>
</html>