secubox-openwrt/package/secubox/luci-app-bandwidth-manager/htdocs/luci-static/resources/view/bandwidth-manager/alerts.js
CyberMind-FR 1bbd345cee refactor(luci): Mass KissTheme UI rework across all LuCI apps
Convert 90+ LuCI view files from legacy cbi-button-* classes to
KissTheme kiss-btn-* classes for consistent dark theme styling.

Pattern conversions applied:
- cbi-button-positive → kiss-btn-green
- cbi-button-negative/remove → kiss-btn-red
- cbi-button-apply → kiss-btn-cyan
- cbi-button-action → kiss-btn-blue
- cbi-button (plain) → kiss-btn

Also replaced hardcoded colors (#080, #c00, #888, etc.) with
CSS variables (--kiss-green, --kiss-red, --kiss-muted, etc.)
for proper dark theme compatibility.

Apps updated include: ai-gateway, auth-guardian, bandwidth-manager,
cloner, config-advisor, crowdsec-dashboard, dns-provider, exposure,
glances, haproxy, hexojs, iot-guard, jellyfin, ksm-manager,
mac-guardian, magicmirror2, master-link, meshname-dns, metablogizer,
metabolizer, mqtt-bridge, netdata-dashboard, picobrew, routes-status,
secubox-admin, secubox-mirror, secubox-p2p, secubox-security-threats,
service-registry, simplex, streamlit, system-hub, tor-shield,
traffic-shaper, vhost-manager, vortex-dns, vortex-firewall,
webradio, wireguard-dashboard, zigbee2mqtt, zkp, and more.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-12 11:09:34 +01:00

384 lines
16 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use strict';
'require view';
'require ui';
'require poll';
'require bandwidth-manager/api as API';
'require secubox/kiss-theme';
return L.view.extend({
handleSaveApply: null,
handleSave: null,
handleReset: null,
settings: {},
history: [],
pending: { alerts: [], count: 0 },
load: function() {
return Promise.all([
API.getAlertSettings(),
API.getAlertHistory(50),
API.getPendingAlerts()
]);
},
renderStats: function() {
var c = KissTheme.colors;
var pendingCount = this.pending.count || 0;
var critical = this.history.filter(function(a) { return a.severity === 'critical'; }).length;
return [
KissTheme.stat(this.settings.enabled ? 'ON' : 'OFF', 'Alerts', this.settings.enabled ? c.green : c.red),
KissTheme.stat(pendingCount, 'Pending', pendingCount > 0 ? c.orange : c.muted),
KissTheme.stat(this.history.length, 'Total', c.blue),
KissTheme.stat(critical, 'Critical', critical > 0 ? c.red : c.muted)
];
},
render: function(data) {
this.settings = data[0] || {};
this.history = (data[1] && data[1].alerts) || [];
this.pending = data[2] || { alerts: [], count: 0 };
var self = this;
var content = [
// Header
E('div', { 'style': 'margin-bottom: 24px;' }, [
E('div', { 'style': 'display: flex; align-items: center; gap: 16px;' }, [
E('h2', { 'style': 'font-size: 24px; font-weight: 700; margin: 0;' }, _('Bandwidth Alerts')),
this.settings.enabled ? KissTheme.badge('Enabled', 'green') : KissTheme.badge('Disabled', 'red')
]),
E('p', { 'style': 'color: var(--kiss-muted); margin: 8px 0 0 0;' },
_('Configure threshold alerts, notification methods, and view alert history'))
]),
// Stats
E('div', { 'class': 'kiss-grid kiss-grid-4', 'style': 'margin: 20px 0;' },
this.renderStats()),
// Pending Alerts Banner
this.pending.count > 0 ? this.renderPendingBanner() : '',
// Two column layout
E('div', { 'class': 'kiss-grid kiss-grid-2', 'style': 'margin-top: 20px;' }, [
this.renderSettingsCard(),
this.renderThresholdsCard()
]),
// Notifications Config
this.renderNotificationsCard(),
// Alert History
this.renderHistoryCard()
];
return KissTheme.wrap(content, 'admin/services/bandwidth-manager/alerts');
},
renderPendingBanner: function() {
var self = this;
return E('div', {
'style': 'display: flex; align-items: center; gap: 16px; padding: 16px 20px; background: linear-gradient(135deg, rgba(245, 158, 11, 0.2) 0%, rgba(239, 68, 68, 0.2) 100%); border: 1px solid var(--kiss-orange); border-radius: 12px; margin-bottom: 20px;'
}, [
E('div', { 'style': 'font-size: 28px;' }, '🔔'),
E('div', { 'style': 'flex: 1;' }, [
E('div', { 'style': 'font-weight: 600; color: var(--kiss-orange);' }, _('%d Pending Alert(s)').format(this.pending.count)),
E('div', { 'style': 'font-size: 13px; color: var(--kiss-muted);' }, _('You have unacknowledged alerts requiring attention'))
]),
E('button', {
'class': 'kiss-btn kiss-btn-orange',
'click': function() { self.scrollToHistory(); }
}, _('View Alerts'))
]);
},
renderSettingsCard: function() {
var self = this;
return KissTheme.card('Alert Settings',
E('div', { 'style': 'display: flex; flex-direction: column; gap: 16px;' }, [
// Main toggle
E('div', { 'style': 'display: flex; justify-content: space-between; align-items: center; padding: 12px; background: var(--kiss-bg); border-radius: 8px;' }, [
E('div', {}, [
E('div', { 'style': 'font-weight: 600;' }, _('Enable Alerts')),
E('div', { 'style': 'font-size: 12px; color: var(--kiss-muted);' }, _('Master switch for all bandwidth alerts'))
]),
E('input', {
'type': 'checkbox',
'id': 'alert-enabled',
'checked': this.settings.enabled,
'style': 'width: 20px; height: 20px;'
})
]),
// Additional alerts
E('div', { 'style': 'padding: 12px; background: var(--kiss-bg); border-radius: 8px;' }, [
E('div', { 'style': 'font-weight: 600; margin-bottom: 12px;' }, '⚡ ' + _('Additional Alerts')),
E('label', { 'style': 'display: flex; align-items: center; gap: 8px; cursor: pointer; margin-bottom: 8px;' }, [
E('input', { 'type': 'checkbox', 'id': 'new-device-alert', 'checked': this.settings.new_device_alert }),
_('New device connected')
]),
E('label', { 'style': 'display: flex; align-items: center; gap: 8px; cursor: pointer;' }, [
E('input', { 'type': 'checkbox', 'id': 'high-bandwidth-alert', 'checked': this.settings.high_bandwidth_alert }),
_('High bandwidth usage above '),
E('input', {
'type': 'number',
'id': 'high-bandwidth-threshold',
'value': this.settings.high_bandwidth_threshold || 100,
'min': '1', 'max': '1000',
'style': 'width: 60px; padding: 4px 8px; background: var(--kiss-bg2); border: 1px solid var(--kiss-line); border-radius: 4px; color: var(--kiss-text); text-align: center;'
}),
_(' Mbps')
])
]),
E('button', {
'class': 'kiss-btn kiss-btn-green',
'click': function() { self.saveAlertSettings(); }
}, _('Save Settings'))
])
);
},
renderThresholdsCard: function() {
return KissTheme.card('📊 ' + _('Quota Thresholds'),
E('div', { 'style': 'display: flex; flex-direction: column; gap: 12px;' }, [
E('label', { 'style': 'display: flex; align-items: center; gap: 12px; padding: 12px; background: var(--kiss-bg); border-radius: 8px; cursor: pointer;' }, [
E('input', { 'type': 'checkbox', 'id': 'threshold-80', 'checked': this.settings.quota_threshold_80 }),
KissTheme.badge('80%', 'blue'),
E('span', {}, _('Warning level'))
]),
E('label', { 'style': 'display: flex; align-items: center; gap: 12px; padding: 12px; background: var(--kiss-bg); border-radius: 8px; cursor: pointer;' }, [
E('input', { 'type': 'checkbox', 'id': 'threshold-90', 'checked': this.settings.quota_threshold_90 }),
KissTheme.badge('90%', 'orange'),
E('span', {}, _('Critical level'))
]),
E('label', { 'style': 'display: flex; align-items: center; gap: 12px; padding: 12px; background: var(--kiss-bg); border-radius: 8px; cursor: pointer;' }, [
E('input', { 'type': 'checkbox', 'id': 'threshold-100', 'checked': this.settings.quota_threshold_100 }),
KissTheme.badge('100%', 'red'),
E('span', {}, _('Quota exceeded'))
])
])
);
},
renderNotificationsCard: function() {
var self = this;
var inputStyle = 'width: 100%; padding: 8px 12px; background: var(--kiss-bg); border: 1px solid var(--kiss-line); border-radius: 6px; color: var(--kiss-text);';
var emailConfigured = this.settings.email && this.settings.email.configured;
var smsConfigured = this.settings.sms && this.settings.sms.configured;
return KissTheme.card(
E('div', { 'style': 'display: flex; justify-content: space-between; align-items: center;' }, [
E('span', {}, '📬 ' + _('Notification Methods')),
E('div', { 'style': 'display: flex; gap: 8px;' }, [
emailConfigured ? KissTheme.badge('Email OK', 'green') : KissTheme.badge('Email', 'muted'),
smsConfigured ? KissTheme.badge('SMS OK', 'green') : KissTheme.badge('SMS', 'muted')
])
]),
E('div', { 'class': 'kiss-grid kiss-grid-2', 'style': 'gap: 20px;' }, [
// Email Config
E('div', { 'style': 'padding: 16px; background: var(--kiss-bg); border-radius: 8px;' }, [
E('div', { 'style': 'font-weight: 600; margin-bottom: 16px;' }, '📧 ' + _('Email Notifications')),
E('div', { 'style': 'display: flex; flex-direction: column; gap: 12px;' }, [
E('input', { 'type': 'text', 'id': 'smtp-server', 'style': inputStyle, 'placeholder': 'smtp.gmail.com',
'value': (this.settings.email && this.settings.email.smtp_server) || '' }),
E('div', { 'style': 'display: grid; grid-template-columns: 1fr 1fr; gap: 12px;' }, [
E('input', { 'type': 'number', 'id': 'smtp-port', 'style': inputStyle, 'placeholder': '587',
'value': (this.settings.email && this.settings.email.smtp_port) || 587 }),
E('label', { 'style': 'display: flex; align-items: center; gap: 8px;' }, [
E('input', { 'type': 'checkbox', 'id': 'smtp-tls', 'checked': this.settings.email ? this.settings.email.smtp_tls : true }),
_('TLS')
])
]),
E('input', { 'type': 'text', 'id': 'smtp-user', 'style': inputStyle, 'placeholder': _('Username'),
'value': (this.settings.email && this.settings.email.smtp_user) || '' }),
E('input', { 'type': 'password', 'id': 'smtp-password', 'style': inputStyle, 'placeholder': _('Password') }),
E('input', { 'type': 'email', 'id': 'email-recipient', 'style': inputStyle, 'placeholder': 'admin@example.com',
'value': (this.settings.email && this.settings.email.recipient) || '' }),
E('div', { 'style': 'display: flex; gap: 8px;' }, [
E('button', { 'class': 'kiss-btn kiss-btn-blue', 'click': function() { self.saveEmailConfig(); } }, _('Save')),
E('button', { 'class': 'kiss-btn', 'click': function() { self.testNotification('email'); } }, _('Test'))
])
])
]),
// SMS Config
E('div', { 'style': 'padding: 16px; background: var(--kiss-bg); border-radius: 8px;' }, [
E('div', { 'style': 'font-weight: 600; margin-bottom: 16px;' }, '📱 ' + _('SMS Notifications')),
E('div', { 'style': 'display: flex; flex-direction: column; gap: 12px;' }, [
E('select', { 'id': 'sms-provider', 'style': inputStyle }, [
E('option', { 'value': '' }, _('Select provider...')),
E('option', { 'value': 'twilio', 'selected': this.settings.sms && this.settings.sms.provider === 'twilio' }, 'Twilio'),
E('option', { 'value': 'nexmo', 'selected': this.settings.sms && this.settings.sms.provider === 'nexmo' }, 'Nexmo/Vonage'),
E('option', { 'value': 'messagebird', 'selected': this.settings.sms && this.settings.sms.provider === 'messagebird' }, 'MessageBird')
]),
E('input', { 'type': 'text', 'id': 'sms-account-sid', 'style': inputStyle, 'placeholder': _('Account SID / API Key') }),
E('input', { 'type': 'password', 'id': 'sms-auth-token', 'style': inputStyle, 'placeholder': _('Auth Token') }),
E('div', { 'style': 'display: grid; grid-template-columns: 1fr 1fr; gap: 12px;' }, [
E('input', { 'type': 'tel', 'id': 'sms-from', 'style': inputStyle, 'placeholder': '+1234567890',
'value': (this.settings.sms && this.settings.sms.from_number) || '' }),
E('input', { 'type': 'tel', 'id': 'sms-to', 'style': inputStyle, 'placeholder': '+1234567890',
'value': (this.settings.sms && this.settings.sms.to_number) || '' })
]),
E('div', { 'style': 'display: flex; gap: 8px;' }, [
E('button', { 'class': 'kiss-btn kiss-btn-blue', 'click': function() { self.saveSmsConfig(); } }, _('Save')),
E('button', { 'class': 'kiss-btn', 'click': function() { self.testNotification('sms'); } }, _('Test'))
])
])
])
])
);
},
renderHistoryCard: function() {
var self = this;
var content;
if (this.history.length === 0) {
content = E('div', { 'style': 'text-align: center; padding: 40px;' }, [
E('div', { 'style': 'font-size: 48px; margin-bottom: 12px; opacity: 0.5;' }, '🔔'),
E('p', { 'style': 'color: var(--kiss-muted);' }, _('No alerts yet')),
E('p', { 'style': 'font-size: 13px; color: var(--kiss-muted);' }, _('Alerts will appear here when quota thresholds are reached'))
]);
} else {
var rows = this.history.slice().reverse().map(function(alert) {
var date = new Date(parseInt(alert.timestamp) * 1000);
var severityColor = alert.severity === 'critical' ? 'red' : (alert.severity === 'warning' ? 'orange' : 'blue');
var icon = alert.severity === 'critical' ? '🚨' : (alert.severity === 'warning' ? '⚠️' : '');
return E('tr', { 'style': alert.acknowledged ? 'opacity: 0.6;' : '' }, [
E('td', { 'style': 'font-size: 20px;' }, icon),
E('td', {}, KissTheme.badge(alert.severity, severityColor)),
E('td', {}, alert.message),
E('td', { 'style': 'color: var(--kiss-muted); font-size: 12px;' }, date.toLocaleString()),
E('td', {}, [
!alert.acknowledged ?
E('button', {
'class': 'kiss-btn kiss-btn-green',
'style': 'padding: 4px 10px; font-size: 11px;',
'click': function() { self.acknowledgeAlert(alert.timestamp); }
}, _('Ack')) :
E('span', { 'style': 'color: var(--kiss-green);' }, '✓')
])
]);
});
content = E('table', { 'class': 'kiss-table' }, [
E('thead', {}, [
E('tr', {}, [
E('th', { 'style': 'width: 40px;' }, ''),
E('th', { 'style': 'width: 80px;' }, _('Severity')),
E('th', {}, _('Message')),
E('th', { 'style': 'width: 150px;' }, _('Time')),
E('th', { 'style': 'width: 60px;' }, '')
])
]),
E('tbody', {}, rows)
]);
}
return KissTheme.card(
E('div', { 'style': 'display: flex; justify-content: space-between; align-items: center;' }, [
E('span', { 'id': 'alert-history' }, '📜 ' + _('Alert History')),
E('div', { 'style': 'display: flex; gap: 8px;' }, [
KissTheme.badge(this.history.length + ' alerts', 'blue'),
E('button', {
'class': 'kiss-btn',
'style': 'padding: 4px 10px; font-size: 11px;',
'click': function() { window.location.reload(); }
}, _('Refresh'))
])
]),
content
);
},
saveAlertSettings: function() {
var enabled = document.getElementById('alert-enabled').checked ? 1 : 0;
var threshold80 = document.getElementById('threshold-80').checked ? 1 : 0;
var threshold90 = document.getElementById('threshold-90').checked ? 1 : 0;
var threshold100 = document.getElementById('threshold-100').checked ? 1 : 0;
var newDevice = document.getElementById('new-device-alert').checked ? 1 : 0;
var highBandwidth = document.getElementById('high-bandwidth-alert').checked ? 1 : 0;
var highThreshold = parseInt(document.getElementById('high-bandwidth-threshold').value) || 100;
API.updateAlertSettings(enabled, threshold80, threshold90, threshold100, newDevice, highBandwidth, highThreshold).then(function(result) {
if (result.success) {
ui.addNotification(null, E('p', _('Alert settings saved')), 'success');
} else {
ui.addNotification(null, E('p', result.message || _('Failed to save settings')), 'error');
}
});
},
saveEmailConfig: function() {
var server = document.getElementById('smtp-server').value;
var port = parseInt(document.getElementById('smtp-port').value) || 587;
var user = document.getElementById('smtp-user').value;
var password = document.getElementById('smtp-password').value;
var tls = document.getElementById('smtp-tls').checked ? 1 : 0;
var recipient = document.getElementById('email-recipient').value;
if (!server || !recipient) {
ui.addNotification(null, E('p', _('SMTP server and recipient are required')), 'error');
return;
}
API.configureEmail(server, port, user, password, tls, recipient, '').then(function(result) {
if (result.success) {
ui.addNotification(null, E('p', _('Email configuration saved')), 'success');
} else {
ui.addNotification(null, E('p', result.message || _('Failed to save email config')), 'error');
}
});
},
saveSmsConfig: function() {
var provider = document.getElementById('sms-provider').value;
var accountSid = document.getElementById('sms-account-sid').value;
var authToken = document.getElementById('sms-auth-token').value;
var fromNumber = document.getElementById('sms-from').value;
var toNumber = document.getElementById('sms-to').value;
if (!provider || !toNumber) {
ui.addNotification(null, E('p', _('Provider and recipient number are required')), 'error');
return;
}
API.configureSms(provider, accountSid, authToken, fromNumber, toNumber).then(function(result) {
if (result.success) {
ui.addNotification(null, E('p', _('SMS configuration saved')), 'success');
} else {
ui.addNotification(null, E('p', result.message || _('Failed to save SMS config')), 'error');
}
});
},
testNotification: function(type) {
ui.addNotification(null, E('p', _('Sending test notification...')), 'info');
API.testNotification(type).then(function(result) {
if (result.success) {
ui.addNotification(null, E('p', _('Test notification sent')), 'success');
} else {
ui.addNotification(null, E('p', result.message || _('Failed to send test notification')), 'error');
}
});
},
acknowledgeAlert: function(timestamp) {
API.acknowledgeAlert(timestamp).then(function(result) {
if (result.success) {
window.location.reload();
}
});
},
scrollToHistory: function() {
var historySection = document.getElementById('alert-history');
if (historySection) {
historySection.scrollIntoView({ behavior: 'smooth' });
}
}
});