diff --git a/src/public/index.html b/src/public/index.html index 0ec008efb..bccc303cb 100644 --- a/src/public/index.html +++ b/src/public/index.html @@ -906,9 +906,6 @@ - @@ -972,6 +969,7 @@ + diff --git a/src/public/js/alertsHandler.js b/src/public/js/alertsHandler.js index 1ae69e2db..65dad71da 100644 --- a/src/public/js/alertsHandler.js +++ b/src/public/js/alertsHandler.js @@ -249,7 +249,11 @@ PulseApp.alerts = (() => { ${SEVERITY_ICONS.info} -

No active alerts

+

No active alerts

+ `; return; @@ -314,6 +318,20 @@ PulseApp.alerts = (() => { `; } + // Add Manage Alerts button to the bottom + content += ` +
+ +
+ `; + alertDropdown.innerHTML = content; // Restore scroll position if acknowledged section exists and was expanded diff --git a/src/public/js/ui/alertManagementModal.js b/src/public/js/ui/alertManagementModal.js new file mode 100644 index 000000000..b6770a6f8 --- /dev/null +++ b/src/public/js/ui/alertManagementModal.js @@ -0,0 +1,1125 @@ +PulseApp.ui = PulseApp.ui || {}; + +PulseApp.ui.alertManagementModal = (() => { + let isInitialized = false; + let activeTab = 'alerts'; + let currentConfig = {}; + let formDataCache = {}; + + function init() { + if (isInitialized) return; + + console.log('[AlertManagementModal] Initializing alert management modal...'); + + // Create modal HTML + createModalHTML(); + + // Set up event listeners + setupEventListeners(); + + isInitialized = true; + console.log('[AlertManagementModal] Alert management modal initialized'); + } + + function createModalHTML() { + const existingModal = document.getElementById('alert-management-modal'); + if (existingModal) { + existingModal.remove(); + } + + const modalHTML = ` + + `; + + document.body.insertAdjacentHTML('beforeend', modalHTML); + } + + function setupEventListeners() { + const modal = document.getElementById('alert-management-modal'); + const closeButton = document.getElementById('alert-management-modal-close'); + const cancelButton = document.getElementById('alert-management-cancel-button'); + const saveButton = document.getElementById('alert-management-save-button'); + + if (closeButton) { + closeButton.addEventListener('click', closeModal); + } + + if (cancelButton) { + cancelButton.addEventListener('click', closeModal); + } + + if (saveButton) { + saveButton.addEventListener('click', saveConfiguration); + } + + // Close modal when clicking outside + if (modal) { + modal.addEventListener('click', (e) => { + if (e.target === modal) { + closeModal(); + } + }); + } + + // Handle escape key + document.addEventListener('keydown', (e) => { + if (e.key === 'Escape' && modal && !modal.classList.contains('hidden')) { + closeModal(); + } + }); + + // Set up tab navigation + setupTabNavigation(); + } + + function setupTabNavigation() { + const tabButtons = document.querySelectorAll('.alert-tab'); + + tabButtons.forEach(button => { + button.addEventListener('click', (e) => { + const tabName = e.target.getAttribute('data-tab'); + switchTab(tabName); + }); + }); + } + + function switchTab(tabName) { + // Preserve current form data before switching tabs + preserveCurrentFormData(); + + activeTab = tabName; + + // Update tab buttons + const tabButtons = document.querySelectorAll('.alert-tab'); + tabButtons.forEach(button => { + const isActive = button.getAttribute('data-tab') === tabName; + + if (isActive) { + button.classList.add('active'); + button.classList.remove('border-transparent', 'text-gray-500', 'dark:text-gray-400'); + button.classList.add('border-blue-500', 'text-blue-600', 'dark:text-blue-400'); + } else { + button.classList.remove('active'); + button.classList.remove('border-blue-500', 'text-blue-600', 'dark:text-blue-400'); + button.classList.add('border-transparent', 'text-gray-500', 'dark:text-gray-400'); + } + }); + + // Update content + renderTabContent(); + + // Restore form data for the new tab + setTimeout(() => { + restoreFormData(tabName); + }, 100); + } + + function renderTabContent() { + const modalBody = document.getElementById('alert-management-modal-body'); + if (!modalBody) return; + + switch (activeTab) { + case 'alerts': + modalBody.innerHTML = renderAlertsTab(); + initializeAlertsTab(); + break; + case 'alert-rules': + modalBody.innerHTML = renderAlertRulesTab(); + initializeAlertRulesTab(); + break; + case 'notifications': + modalBody.innerHTML = renderNotificationsTab(); + initializeNotificationsTab(); + break; + default: + modalBody.innerHTML = '

Unknown tab

'; + } + } + + function renderAlertsTab() { + return ` +
+
+

Current Alerts

+ +
+ +
+ +

Loading current alerts...

+
+ +
+

Quick Actions

+
+ + +
+
+
+ `; + } + + function renderAlertRulesTab() { + return ` +
+
+
+

Alert Rules

+

Manage system and custom alert rules

+
+
+ + +
+
+ + +
+
+
+ + + +

System Alerts

+ Built-in +
+

Core monitoring alerts that cannot be deleted

+
+
+ +

Loading system alerts...

+
+
+ + +
+
+
+ + + +

Custom Alerts

+ User-created +
+

Custom threshold and rule-based alerts

+
+
+ +

Loading custom alerts...

+
+
+
+ `; + } + + function renderNotificationsTab() { + const config = currentConfig || {}; + const smtp = config.advanced?.smtp || {}; + + return ` +
+
+

Notification Settings

+

Configure how alerts are delivered beyond the Pulse UI

+
+ + +
+

Global Defaults

+

Choose where all alerts should be sent by default. Individual rules can override these settings.

+ +
+ + + +
+
+ + +
+

Email Configuration

+ + +
+

Email configuration will be loaded...

+
+
+ + +
+

Webhook Configuration

+ +
+
+ + +

+ Supports Discord, Slack, Microsoft Teams, and custom webhooks +

+
+ +
+ +
+
+
+ + +
+

Per-Rule Overrides

+

+ Advanced users can customize notification delivery for specific alert rules. + Go to the Alert Rules tab and click on individual rules to configure custom delivery settings. +

+ +
+
+ `; + } + + function initializeAlertsTab() { + // Set up refresh button + const refreshBtn = document.getElementById('refresh-alerts-btn'); + if (refreshBtn) { + refreshBtn.addEventListener('click', loadCurrentAlerts); + } + + // Set up action buttons + const acknowledgeAllBtn = document.getElementById('acknowledge-all-alerts-btn'); + if (acknowledgeAllBtn) { + acknowledgeAllBtn.addEventListener('click', () => { + if (PulseApp.alerts && PulseApp.alerts.markAllAsAcknowledged) { + PulseApp.alerts.markAllAsAcknowledged(); + setTimeout(loadCurrentAlerts, 500); // Refresh after acknowledging + } + }); + } + + const viewHistoryBtn = document.getElementById('view-alert-history-btn'); + if (viewHistoryBtn) { + viewHistoryBtn.addEventListener('click', () => { + // TODO: Implement alert history view + console.log('Alert history view not yet implemented'); + }); + } + + // Load current alerts + loadCurrentAlerts(); + } + + function initializeAlertRulesTab() { + // Set up filter dropdown + const filterSelect = document.getElementById('alert-rules-filter'); + if (filterSelect) { + filterSelect.addEventListener('change', filterAlertRules); + } + + // Set up add custom alert button + const addCustomBtn = document.getElementById('add-custom-alert-btn'); + if (addCustomBtn) { + addCustomBtn.addEventListener('click', openCustomAlertModal); + } + + // Load system and custom alerts + loadSystemAlerts(); + loadCustomAlerts(); + } + + function initializeNotificationsTab() { + // Load email configuration into the email section + loadEmailConfiguration(); + + // Set up webhook test button + const testWebhookBtn = document.getElementById('test-webhook-btn'); + if (testWebhookBtn) { + testWebhookBtn.addEventListener('click', testWebhookConnection); + } + } + + function filterAlertRules() { + const filterValue = document.getElementById('alert-rules-filter')?.value; + // TODO: Implement filtering logic + console.log('Filtering alert rules by:', filterValue); + } + + function loadSystemAlerts() { + const systemAlertsContent = document.getElementById('system-alerts-content'); + if (!systemAlertsContent) return; + + // Create system alert rules (CPU, Memory, Disk, Down) + const systemAlerts = [ + { id: 'cpu', name: 'CPU Usage', description: 'Alert when CPU usage exceeds threshold', enabled: true, threshold: 85 }, + { id: 'memory', name: 'Memory Usage', description: 'Alert when memory usage exceeds threshold', enabled: true, threshold: 90 }, + { id: 'disk', name: 'Disk Usage', description: 'Alert when disk usage exceeds threshold', enabled: true, threshold: 95 }, + { id: 'down', name: 'System Down', description: 'Alert when VM/LXC goes offline', enabled: true, threshold: null } + ]; + + systemAlertsContent.innerHTML = systemAlerts.map(alert => createSystemAlertCard(alert)).join(''); + } + + function loadCustomAlerts() { + const customAlertsContent = document.getElementById('custom-alerts-content'); + if (!customAlertsContent) return; + + // Load custom alerts from settings if available + if (PulseApp.ui.settings && PulseApp.ui.settings.loadThresholdConfigurations) { + // This will populate custom threshold configurations + PulseApp.ui.settings.loadThresholdConfigurations(); + } + + // For now, show placeholder + customAlertsContent.innerHTML = ` +
+ + + +

No custom alerts configured

+ +
+ `; + } + + function createSystemAlertCard(alert) { + const deliveryBadges = ` +
+ + Pulse + + + Email + + + Webhook + +
+ `; + + return ` +
+
+
+
${alert.name}
+ ${alert.enabled ? + 'Enabled' : + 'Disabled' + } +
+

${alert.description}

+ ${deliveryBadges} +
+
+ ${alert.threshold ? ` + ${alert.threshold}% + ` : ''} + +
+
+ `; + } + + function loadEmailConfiguration() { + const emailConfigSection = document.getElementById('email-config-section'); + if (!emailConfigSection) return; + + // Use existing email configuration from settings if available + if (PulseApp.ui.settings && PulseApp.ui.settings.renderAlertsTab) { + const config = currentConfig || {}; + const fullContent = PulseApp.ui.settings.renderAlertsTab(config.alerts || {}, config); + + // Extract just the email configuration section + const tempDiv = document.createElement('div'); + tempDiv.innerHTML = fullContent; + const emailSection = tempDiv.querySelector('.bg-gray-50:nth-child(2)'); // Email section + + if (emailSection) { + // Remove the outer container and just show the inner content + const emailContent = emailSection.innerHTML; + emailConfigSection.innerHTML = emailContent; + + // Initialize email functionality + if (PulseApp.ui.settings.setupEmailProviderSelection) { + setTimeout(() => PulseApp.ui.settings.setupEmailProviderSelection(), 100); + } + if (PulseApp.ui.settings.setupEmailTestButton) { + setTimeout(() => PulseApp.ui.settings.setupEmailTestButton(), 100); + } + } + } + } + + function testWebhookConnection() { + const webhookUrl = document.querySelector('input[name="WEBHOOK_URL"]')?.value; + if (!webhookUrl) { + alert('Please enter a webhook URL first'); + return; + } + + // Use existing webhook test functionality from settings if available + if (PulseApp.ui.settings && PulseApp.ui.settings.setupWebhookTestButton) { + PulseApp.ui.settings.setupWebhookTestButton(); + } else { + // Fallback test + console.log('Testing webhook:', webhookUrl); + alert('Webhook test functionality will be implemented'); + } + } + + function openCustomAlertModal(presetThresholds = null) { + // Check if settings modal has the threshold functionality we can reuse + if (PulseApp.ui.settings && PulseApp.ui.settings.showThresholdModal && !presetThresholds) { + // Use the existing threshold modal from settings (only if no presets) + PulseApp.ui.settings.showThresholdModal(); + } else { + // Create our own custom alert modal with optional preset values + createCustomAlertModal(presetThresholds); + } + } + + function createCustomAlertModal(presetThresholds = null) { + // Remove any existing custom alert modal + const existingModal = document.getElementById('custom-alert-modal'); + if (existingModal) { + existingModal.remove(); + } + + // Generate alert name and determine best metric from presets + let suggestedName = 'Custom Alert'; + let primaryMetric = 'cpu'; + let primaryThreshold = 85; + let multipleThresholds = false; + + if (presetThresholds && presetThresholds.length > 0) { + // Create a descriptive name based on active thresholds + const thresholdNames = presetThresholds.map(t => `${t.type.toUpperCase()}: ${t.value}${t.type === 'cpu' || t.type === 'memory' || t.type === 'disk' ? '%' : ''}`); + suggestedName = `Alert for ${thresholdNames.join(', ')}`; + + // Use the first threshold as the primary one for the form + primaryMetric = presetThresholds[0].type; + primaryThreshold = presetThresholds[0].value; + + // Track if we have multiple thresholds + multipleThresholds = presetThresholds.length > 1; + } + + const modalHTML = ` +
+ +
+ `; + + document.body.insertAdjacentHTML('beforeend', modalHTML); + setupCustomAlertModalEvents(); + } + + function setupCustomAlertModalEvents() { + const modal = document.getElementById('custom-alert-modal'); + const closeBtn = document.getElementById('custom-alert-modal-close'); + const cancelBtn = document.getElementById('custom-alert-cancel-button'); + const saveBtn = document.getElementById('custom-alert-save-button'); + const targetTypeSelect = document.querySelector('select[name="targetType"]'); + const metricSelect = document.querySelector('select[name="metric"]'); + + // Close modal events + [closeBtn, cancelBtn].forEach(btn => { + if (btn) { + btn.addEventListener('click', () => { + modal.remove(); + }); + } + }); + + // Close on outside click + modal.addEventListener('click', (e) => { + if (e.target === modal) { + modal.remove(); + } + }); + + // Handle target type changes + if (targetTypeSelect) { + targetTypeSelect.addEventListener('change', (e) => { + const specificSection = document.getElementById('specific-target-section'); + if (e.target.value === 'specific' || e.target.value === 'node') { + specificSection.classList.remove('hidden'); + const input = specificSection.querySelector('input'); + input.required = true; + input.placeholder = e.target.value === 'node' ? 'e.g., pve-node-1' : 'e.g., 100 or vm-name'; + } else { + specificSection.classList.add('hidden'); + specificSection.querySelector('input').required = false; + } + }); + } + + // Handle metric changes + if (metricSelect) { + metricSelect.addEventListener('change', (e) => { + const thresholdSection = document.getElementById('threshold-section'); + const thresholdInput = document.querySelector('input[name="threshold"]'); + const operatorSelect = document.querySelector('select[name="operator"]'); + const percentSpan = thresholdSection.querySelector('span'); + + if (e.target.value === 'status') { + thresholdSection.style.display = 'none'; + thresholdInput.required = false; + } else { + thresholdSection.style.display = 'block'; + thresholdInput.required = true; + + // Update placeholder and limits based on metric + switch (e.target.value) { + case 'cpu': + thresholdInput.placeholder = '85'; + thresholdInput.max = '100'; + percentSpan.textContent = '%'; + break; + case 'memory': + thresholdInput.placeholder = '90'; + thresholdInput.max = '100'; + percentSpan.textContent = '%'; + break; + case 'disk': + thresholdInput.placeholder = '95'; + thresholdInput.max = '100'; + percentSpan.textContent = '%'; + break; + case 'network': + thresholdInput.placeholder = '100'; + thresholdInput.max = '1000'; + percentSpan.textContent = 'MB/s'; + break; + } + } + }); + } + + // Handle save + if (saveBtn) { + saveBtn.addEventListener('click', saveCustomAlert); + } + } + + function saveCustomAlert() { + const form = document.getElementById('custom-alert-form'); + const formData = new FormData(form); + + // Validate form + if (!form.checkValidity()) { + form.reportValidity(); + return; + } + + // Check if we have multiple thresholds from the dashboard + const multipleThresholdsData = formData.get('multipleThresholds'); + let thresholds = []; + + if (multipleThresholdsData) { + // Parse the JSON data containing all the preset thresholds + try { + thresholds = JSON.parse(multipleThresholdsData.replace(/"/g, '"')); + console.log('Using multiple thresholds from dashboard:', thresholds); + } catch (error) { + console.warn('Failed to parse multiple thresholds data:', error); + } + } + + // If no multiple thresholds, use the single threshold from the form + if (thresholds.length === 0) { + const threshold = formData.get('threshold') ? parseFloat(formData.get('threshold')) : null; + if (threshold !== null) { + thresholds = [{ + type: formData.get('metric'), + operator: formData.get('operator') || '>', + value: threshold + }]; + } + } + + // Build alert configuration + const alertConfig = { + name: formData.get('alertName'), + targetType: formData.get('targetType'), + specificTarget: formData.get('specificTarget'), + thresholds: thresholds, // Use array of thresholds instead of single values + sendEmail: formData.has('sendEmail'), + sendWebhook: formData.has('sendWebhook'), + enabled: true, + createdAt: Date.now() + }; + + console.log('Creating custom alert:', alertConfig); + + // TODO: Save to backend + // For now, just show success and close modal + const thresholdSummary = thresholds.map(t => `${t.type.toUpperCase()}: ${t.value}${['cpu', 'memory', 'disk'].includes(t.type) ? '%' : ''}`).join(', '); + alert(`Custom alert created successfully!\n\nAlert: ${alertConfig.name}\nThresholds: ${thresholdSummary}\n\nNote: Backend integration still needs to be implemented.`); + + // Close modal + document.getElementById('custom-alert-modal').remove(); + + // Refresh the custom alerts list + loadCustomAlerts(); + } + + function loadCurrentAlerts() { + const contentDiv = document.getElementById('current-alerts-content'); + if (!contentDiv) return; + + // Get alerts from the alerts handler + if (PulseApp.alerts && PulseApp.alerts.getActiveAlerts) { + const activeAlerts = PulseApp.alerts.getActiveAlerts(); + renderCurrentAlerts(activeAlerts); + } else { + contentDiv.innerHTML = '

Alert system not initialized

'; + } + } + + function renderCurrentAlerts(alerts) { + const contentDiv = document.getElementById('current-alerts-content'); + if (!contentDiv) return; + + if (!alerts || alerts.length === 0) { + contentDiv.innerHTML = ` +
+ + + +

No Active Alerts

+

All systems are running normally.

+
+ `; + return; + } + + const unacknowledgedAlerts = alerts.filter(alert => !alert.acknowledged); + const acknowledgedAlerts = alerts.filter(alert => alert.acknowledged); + + let content = ''; + + if (unacknowledgedAlerts.length > 0) { + content += ` +
+

+ Unacknowledged Alerts (${unacknowledgedAlerts.length}) +

+ ${unacknowledgedAlerts.map(alert => renderAlertCard(alert, false)).join('')} +
+ `; + } + + if (acknowledgedAlerts.length > 0) { + content += ` +
+

+ Acknowledged Alerts (${acknowledgedAlerts.length}) +

+ ${acknowledgedAlerts.map(alert => renderAlertCard(alert, true)).join('')} +
+ `; + } + + contentDiv.innerHTML = content; + } + + function renderAlertCard(alert, acknowledged = false) { + const severityColors = { + 'critical': 'border-red-400 bg-red-50 dark:bg-red-900/10', + 'warning': 'border-yellow-400 bg-yellow-50 dark:bg-yellow-900/10', + 'info': 'border-blue-400 bg-blue-50 dark:bg-blue-900/10' + }; + + const severity = alert.severity || 'info'; + const colorClass = severityColors[severity] || severityColors.info; + + const duration = Math.round((Date.now() - alert.triggeredAt) / 1000); + const durationStr = duration < 60 ? `${duration}s` : + duration < 3600 ? `${Math.round(duration/60)}m` : + `${Math.round(duration/3600)}h`; + + const acknowledgedClass = acknowledged ? 'opacity-60' : ''; + + let currentValueDisplay = ''; + if (alert.metric === 'status') { + currentValueDisplay = alert.currentValue; + } else if (typeof alert.currentValue === 'number') { + const isPercentageMetric = ['cpu', 'memory', 'disk'].includes(alert.metric); + currentValueDisplay = `${Math.round(alert.currentValue)}${isPercentageMetric ? '%' : ''}`; + } else { + currentValueDisplay = alert.currentValue; + } + + // Delivery indicators - Pulse UI is always present + const deliveryIndicators = ` +
+ Delivered to: + + Pulse + + ${alert.emailSent ? 'Email' : ''} + ${alert.webhookSent ? 'Webhook' : ''} +
+ `; + + return ` +
+
+
+
+
+ ${alert.guest?.name || 'Unknown'} +
+ + ${currentValueDisplay} + + ${alert.escalated ? 'Escalated' : ''} + ${acknowledged ? 'Acknowledged' : ''} +
+ ${deliveryIndicators} +

+ ${alert.ruleName || 'Unknown Rule'} +

+

+ Active for ${durationStr} + ${acknowledged ? ` • Acknowledged ${Math.round((Date.now() - alert.acknowledgedAt) / 60000)}m ago` : ''} +

+
+
+ ${!acknowledged ? ` + + ` : ''} + +
+
+
+ `; + } + + function preserveCurrentFormData() { + // TODO: Implement form data preservation + } + + function restoreFormData(tabName) { + // TODO: Implement form data restoration + } + + function openModal(targetTab = null) { + const modal = document.getElementById('alert-management-modal'); + if (modal) { + modal.classList.remove('hidden'); + modal.classList.add('flex'); + + // Load current configuration if needed + loadConfiguration(); + + // Switch to target tab if specified + if (targetTab && targetTab !== activeTab) { + switchTab(targetTab); + } else { + // Render initial tab content + renderTabContent(); + } + } + } + + function closeModal() { + const modal = document.getElementById('alert-management-modal'); + if (modal) { + modal.classList.add('hidden'); + modal.classList.remove('flex'); + } + } + + async function loadConfiguration() { + try { + console.log('Loading alert configuration...'); + + // Use the same configuration loading logic as settings + if (PulseApp.ui.settings && PulseApp.ui.settings.getCurrentConfig) { + currentConfig = await PulseApp.ui.settings.getCurrentConfig(); + } else { + // Fallback: load configuration directly + const response = await fetch('/api/config'); + if (response.ok) { + currentConfig = await response.json(); + } else { + console.error('Failed to load configuration'); + currentConfig = {}; + } + } + } catch (error) { + console.error('Error loading configuration:', error); + currentConfig = {}; + } + } + + async function saveConfiguration() { + // TODO: Save alert configuration + console.log('Saving alert configuration...'); + } + + // Global functions that need to be accessible from HTML onclick handlers + window.toggleSystemAlert = function(alertId, enabled) { + console.log(`Toggling system alert ${alertId} to ${enabled ? 'enabled' : 'disabled'}`); + // TODO: Implement system alert toggle functionality + // This would update the configuration and save it + }; + + // Public API + return { + init, + openModal, + closeModal, + switchTab: switchTab, + openCustomAlertModal, + loadCurrentAlerts: () => { + if (activeTab === 'alerts') { + loadCurrentAlerts(); + } + } + }; +})(); + +// Auto-initialize when DOM is ready +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', PulseApp.ui.alertManagementModal.init); +} else { + PulseApp.ui.alertManagementModal.init(); +} \ No newline at end of file diff --git a/src/public/js/ui/settings.js b/src/public/js/ui/settings.js index 3790cd836..770d3585a 100644 --- a/src/public/js/ui/settings.js +++ b/src/public/js/ui/settings.js @@ -179,9 +179,6 @@ PulseApp.ui.settings = (() => { case 'system': content = renderSystemTab(advanced, safeConfig); break; - case 'alert-management': - content = renderAlertManagementTab(); - break; case 'diagnostics': content = renderDiagnosticsTab(); break; @@ -212,9 +209,6 @@ PulseApp.ui.settings = (() => { } else if (activeTab === 'alerts') { // Load threshold configurations when alerts tab is opened loadThresholdConfigurations(); - } else if (activeTab === 'alert-management') { - // Initialize alert management tab - initializeAlertManagementTab(); } } @@ -4325,7 +4319,16 @@ PulseApp.ui.settings = (() => { switchToChannel, acknowledgeStableChoice, proceedWithStableSwitch, - clearUpdateCache + clearUpdateCache, + // Expose alert-related functions for the alert management modal + renderAlertsTab, + renderAlertManagementTab, + loadThresholdConfigurations, + setupEmailProviderSelection, + setupEmailTestButton, + setupWebhookTestButton, + initializeAlertManagementTab, + getCurrentConfig: () => currentConfig }; })(); diff --git a/src/public/js/ui/thresholds.js b/src/public/js/ui/thresholds.js index 59db36b04..f5d13e2d4 100644 --- a/src/public/js/ui/thresholds.js +++ b/src/public/js/ui/thresholds.js @@ -138,16 +138,21 @@ PulseApp.ui.thresholds = (() => { return; } - // Show modal for creating alert rule - _showCreateAlertRuleModal(activeThresholds); + // Open the custom alert creation modal with pre-populated threshold values + if (PulseApp.ui && PulseApp.ui.alertManagementModal && PulseApp.ui.alertManagementModal.openCustomAlertModal) { + PulseApp.ui.alertManagementModal.openCustomAlertModal(activeThresholds); + } else { + // Fallback - show legacy modal if alert management isn't available + _showCreateAlertRuleModal(activeThresholds); + } } function _handleViewAlertRules() { - // Open the unified Alert Management interface in settings - if (PulseApp.ui && PulseApp.ui.settings && PulseApp.ui.settings.openModalWithTab) { - PulseApp.ui.settings.openModalWithTab('alert-management'); + // Open the Alert Management modal directly to the Alert Rules tab + if (PulseApp.ui && PulseApp.ui.alertManagementModal && PulseApp.ui.alertManagementModal.openModal) { + PulseApp.ui.alertManagementModal.openModal('alert-rules'); } else { - // Fallback to the old modal if settings isn't available + // Fallback to the old modal if alert management isn't available _showViewAlertRulesModal(); } }