secubox-openwrt/package/secubox/luci-app-secubox/htdocs/luci-static/resources/view/secubox/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

381 lines
14 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 rpc';
'require poll';
'require secubox/kiss-theme';
/**
* SecuBox Alerts - KISS Edition
* System alerts management with inline CSS
*/
var callGetAlerts = rpc.declare({
object: 'luci.secubox',
method: 'get_alerts',
expect: {}
});
var callDismissAlert = rpc.declare({
object: 'luci.secubox',
method: 'dismiss_alert',
params: ['alert_id'],
expect: {}
});
var callClearAlerts = rpc.declare({
object: 'luci.secubox',
method: 'clear_alerts',
expect: {}
});
return view.extend({
alerts: [],
filterSeverity: 'all',
filterModule: 'all',
sortBy: 'time',
load: function() {
var self = this;
return callGetAlerts().then(function(data) {
self.alerts = (data && data.alerts) || [];
return self.alerts;
}).catch(function() { return []; });
},
render: function() {
var self = this;
poll.add(function() {
return callGetAlerts().then(function(data) {
self.alerts = (data && data.alerts) || [];
self.updateDisplay();
});
}, 30);
var content = E('div', { 'class': 'sb-alerts' }, [
E('style', {}, this.getStyles()),
this.renderHeader(),
this.renderControls(),
E('div', { 'id': 'alerts-list', 'class': 'sb-alerts-list' },
this.renderAlertsList())
]);
return KissTheme.wrap(content, 'admin/secubox/alerts');
},
renderHeader: function() {
var stats = this.getStats();
var chips = [
{ icon: '📊', label: 'Total', value: stats.total },
{ icon: '❌', label: 'Errors', value: stats.errors, color: stats.errors > 0 ? '#ef4444' : '' },
{ icon: '⚠️', label: 'Warnings', value: stats.warnings, color: stats.warnings > 0 ? '#f59e0b' : '' },
{ icon: '', label: 'Info', value: stats.info }
];
var self = this;
return E('div', { 'class': 'sb-header' }, [
E('div', {}, [
E('h2', { 'class': 'sb-title' }, '⚠️ System Alerts'),
E('p', { 'class': 'sb-subtitle' }, 'Monitor and manage system notifications')
]),
E('div', { 'class': 'sb-header-right' }, [
E('div', { 'class': 'sb-chips', 'id': 'header-chips' }, chips.map(function(c) {
return E('div', { 'class': 'sb-chip', 'data-chip': c.label.toLowerCase() }, [
E('span', { 'class': 'sb-chip-icon' }, c.icon),
E('div', {}, [
E('span', { 'class': 'sb-chip-label' }, c.label),
E('strong', { 'style': c.color ? 'color:' + c.color : '' }, String(c.value))
])
]);
})),
E('div', { 'class': 'sb-header-actions' }, [
E('button', {
'class': 'sb-btn',
'click': function() { self.refreshAlerts(); }
}, '🔄 Refresh'),
E('button', {
'class': 'sb-btn sb-btn-danger',
'click': function() { self.clearAllAlerts(); }
}, '🗑️ Clear All')
])
])
]);
},
renderControls: function() {
var self = this;
var modules = this.getUniqueModules();
return E('div', { 'class': 'sb-controls' }, [
E('div', { 'class': 'sb-control-group' }, [
E('label', {}, 'Severity'),
E('select', {
'id': 'filter-severity',
'class': 'sb-select',
'change': function(ev) {
self.filterSeverity = ev.target.value;
self.updateDisplay();
}
}, [
E('option', { 'value': 'all' }, 'All'),
E('option', { 'value': 'error' }, '❌ Error'),
E('option', { 'value': 'warning' }, '⚠️ Warning'),
E('option', { 'value': 'info' }, ' Info')
])
]),
E('div', { 'class': 'sb-control-group' }, [
E('label', {}, 'Module'),
E('select', {
'id': 'filter-module',
'class': 'sb-select',
'change': function(ev) {
self.filterModule = ev.target.value;
self.updateDisplay();
}
}, [E('option', { 'value': 'all' }, 'All Modules')].concat(
modules.map(function(m) { return E('option', { 'value': m }, m); })
))
]),
E('div', { 'class': 'sb-control-group' }, [
E('label', {}, 'Sort'),
E('select', {
'class': 'sb-select',
'change': function(ev) {
self.sortBy = ev.target.value;
self.updateDisplay();
}
}, [
E('option', { 'value': 'time' }, 'Newest first'),
E('option', { 'value': 'severity' }, 'Severity'),
E('option', { 'value': 'module' }, 'Module')
])
])
]);
},
renderAlertsList: function() {
var filtered = this.getFilteredAlerts();
if (filtered.length === 0) {
return [E('div', { 'class': 'sb-empty' }, [
E('span', {}, this.alerts.length === 0 ? '✓' : '🔍'),
E('h3', {}, this.alerts.length === 0 ? 'No Alerts' : 'No Matching Alerts'),
E('p', {}, this.alerts.length === 0 ? 'All systems operating normally' : 'Try adjusting filters')
])];
}
var self = this;
return filtered.map(function(alert) { return self.renderAlertItem(alert); });
},
renderAlertItem: function(alert) {
var self = this;
var sev = alert.severity || 'info';
var sevIcon = sev === 'error' ? '❌' : sev === 'warning' ? '⚠️' : '';
var sevColor = sev === 'error' ? '#ef4444' : sev === 'warning' ? '#f59e0b' : '#3b82f6';
var timeAgo = this.formatTimeAgo(alert.timestamp);
var alertId = (alert.module || 'system') + '_' + (alert.timestamp || Date.now());
return E('div', { 'class': 'sb-alert-item sb-alert-' + sev }, [
E('div', { 'class': 'sb-alert-icon', 'style': 'background:' + sevColor }, sevIcon),
E('div', { 'class': 'sb-alert-content' }, [
E('div', { 'class': 'sb-alert-header' }, [
E('strong', {}, alert.module || 'System'),
E('span', { 'class': 'sb-alert-time' }, timeAgo)
]),
E('p', { 'class': 'sb-alert-message' }, alert.message || 'No message'),
E('div', { 'class': 'sb-alert-footer' }, [
E('span', { 'class': 'sb-badge sb-badge-' + sev }, sev.toUpperCase())
])
]),
E('button', {
'class': 'sb-alert-dismiss',
'title': 'Dismiss',
'click': function() { self.dismissAlert(alertId, alert); }
}, '×')
]);
},
getFilteredAlerts: function() {
var self = this;
var filtered = this.alerts.filter(function(a) {
var sevMatch = self.filterSeverity === 'all' || a.severity === self.filterSeverity;
var modMatch = self.filterModule === 'all' || a.module === self.filterModule;
return sevMatch && modMatch;
});
filtered.sort(function(a, b) {
if (self.sortBy === 'time') return (b.timestamp || 0) - (a.timestamp || 0);
if (self.sortBy === 'severity') {
var order = { error: 3, warning: 2, info: 1 };
return (order[b.severity] || 0) - (order[a.severity] || 0);
}
if (self.sortBy === 'module') return (a.module || '').localeCompare(b.module || '');
return 0;
});
return filtered;
},
getStats: function() {
var alerts = this.alerts;
return {
total: alerts.length,
errors: alerts.filter(function(a) { return a.severity === 'error'; }).length,
warnings: alerts.filter(function(a) { return a.severity === 'warning'; }).length,
info: alerts.filter(function(a) { return a.severity === 'info'; }).length
};
},
getUniqueModules: function() {
var modules = {};
this.alerts.forEach(function(a) { if (a.module) modules[a.module] = true; });
return Object.keys(modules).sort();
},
formatTimeAgo: function(timestamp) {
if (!timestamp) return 'Unknown';
var now = Math.floor(Date.now() / 1000);
var diff = now - timestamp;
if (diff < 60) return 'Just now';
if (diff < 3600) return Math.floor(diff / 60) + 'm ago';
if (diff < 86400) return Math.floor(diff / 3600) + 'h ago';
if (diff < 604800) return Math.floor(diff / 86400) + 'd ago';
return new Date(timestamp * 1000).toLocaleDateString();
},
updateDisplay: function() {
var list = document.getElementById('alerts-list');
if (list) {
list.innerHTML = '';
this.renderAlertsList().forEach(function(el) { list.appendChild(el); });
}
this.updateHeaderStats();
this.updateModuleFilter();
},
updateHeaderStats: function() {
var stats = this.getStats();
var updates = { 'total': stats.total, 'errors': stats.errors, 'warnings': stats.warnings, 'info': stats.info };
Object.keys(updates).forEach(function(key) {
var chip = document.querySelector('[data-chip="' + key + '"] strong');
if (chip) chip.textContent = String(updates[key]);
});
},
updateModuleFilter: function() {
var select = document.getElementById('filter-module');
if (!select) return;
var current = select.value;
var modules = this.getUniqueModules();
select.innerHTML = '';
select.appendChild(E('option', { 'value': 'all' }, 'All Modules'));
modules.forEach(function(m) { select.appendChild(E('option', { 'value': m }, m)); });
select.value = current;
},
dismissAlert: function(alertId, alert) {
var self = this;
callDismissAlert(alertId).then(function() {
self.alerts = self.alerts.filter(function(a) {
var id = (a.module || 'system') + '_' + (a.timestamp || 0);
return id !== alertId;
});
self.updateDisplay();
ui.addNotification(null, E('p', {}, 'Alert dismissed'), 'info');
}).catch(function(err) {
ui.addNotification(null, E('p', {}, 'Failed: ' + err.message), 'error');
});
},
refreshAlerts: function() {
var self = this;
callGetAlerts().then(function(data) {
self.alerts = (data && data.alerts) || [];
self.updateDisplay();
ui.addNotification(null, E('p', {}, 'Alerts refreshed'), 'info');
});
},
clearAllAlerts: function() {
var self = this;
ui.showModal('Clear All Alerts', [
E('p', {}, 'Are you sure you want to clear all alerts?'),
E('div', { 'class': 'right' }, [
E('button', { 'class': 'kiss-btn', 'click': ui.hideModal }, 'Cancel'),
E('button', {
'class': 'kiss-btn kiss-btn-red',
'click': function() {
callClearAlerts().then(function() {
self.alerts = [];
self.updateDisplay();
ui.hideModal();
ui.addNotification(null, E('p', {}, 'All alerts cleared'), 'info');
});
}
}, 'Clear All')
])
]);
},
getStyles: function() {
return `
.sb-alerts { max-width: 1200px; margin: 0 auto; padding: 20px; }
.sb-header { display: flex; justify-content: space-between; align-items: flex-start; flex-wrap: wrap; gap: 16px; margin-bottom: 20px; padding: 20px; background: #fff; border-radius: 12px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.sb-header-right { display: flex; flex-direction: column; gap: 12px; align-items: flex-end; }
.sb-header-actions { display: flex; gap: 8px; }
.sb-title { margin: 0; font-size: 24px; font-weight: 700; }
.sb-subtitle { margin: 4px 0 0; color: #666; font-size: 14px; }
.sb-chips { display: flex; gap: 12px; flex-wrap: wrap; }
.sb-chip { display: flex; align-items: center; gap: 8px; padding: 8px 12px; background: #f8f9fa; border: 1px solid #e5e7eb; border-radius: 8px; }
.sb-chip-icon { font-size: 16px; }
.sb-chip-label { font-size: 11px; color: #666; display: block; }
.sb-controls { display: flex; gap: 16px; flex-wrap: wrap; margin-bottom: 20px; padding: 16px; background: #fff; border-radius: 10px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.sb-control-group { display: flex; flex-direction: column; gap: 4px; }
.sb-control-group label { font-size: 12px; color: #666; font-weight: 500; }
.sb-select { padding: 8px 12px; border: 1px solid #e5e7eb; border-radius: 6px; font-size: 13px; min-width: 140px; }
.sb-btn { padding: 8px 14px; border-radius: 6px; font-size: 12px; font-weight: 600; cursor: pointer; border: 1px solid #e5e7eb; background: #f8f9fa; color: #333; display: inline-flex; align-items: center; gap: 4px; }
.sb-btn:hover { background: #e5e7eb; }
.sb-btn-danger { background: #fef2f2; color: #ef4444; border-color: #fecaca; }
.sb-btn-danger:hover { background: #fee2e2; }
.sb-alerts-list { display: flex; flex-direction: column; gap: 12px; }
.sb-alert-item { display: flex; gap: 16px; padding: 16px; background: #fff; border-radius: 10px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); position: relative; }
.sb-alert-item.sb-alert-error { border-left: 4px solid #ef4444; }
.sb-alert-item.sb-alert-warning { border-left: 4px solid #f59e0b; }
.sb-alert-item.sb-alert-info { border-left: 4px solid #3b82f6; }
.sb-alert-icon { width: 36px; height: 36px; border-radius: 8px; display: flex; align-items: center; justify-content: center; color: #fff; font-size: 16px; flex-shrink: 0; }
.sb-alert-content { flex: 1; }
.sb-alert-header { display: flex; justify-content: space-between; margin-bottom: 6px; }
.sb-alert-header strong { font-size: 14px; }
.sb-alert-time { font-size: 12px; color: #888; }
.sb-alert-message { font-size: 13px; color: #555; margin: 0 0 8px; }
.sb-alert-footer { display: flex; gap: 8px; }
.sb-badge { font-size: 10px; padding: 2px 8px; border-radius: 4px; font-weight: 600; }
.sb-badge-error { background: #fef2f2; color: #ef4444; }
.sb-badge-warning { background: #fffbeb; color: #f59e0b; }
.sb-badge-info { background: #eff6ff; color: #3b82f6; }
.sb-alert-dismiss { position: absolute; top: 12px; right: 12px; width: 24px; height: 24px; border: none; background: #f0f0f0; border-radius: 4px; cursor: pointer; font-size: 16px; color: #888; }
.sb-alert-dismiss:hover { background: #e5e7eb; color: #333; }
.sb-empty { text-align: center; padding: 60px 20px; color: #888; background: #fff; border-radius: 10px; }
.sb-empty span { font-size: 48px; display: block; margin-bottom: 16px; }
.sb-empty h3 { margin: 0 0 8px; font-size: 18px; color: #333; }
.sb-empty p { margin: 0; }
@media (prefers-color-scheme: dark) {
.sb-alerts { color: #e5e7eb; }
.sb-header, .sb-controls, .sb-alert-item, .sb-empty { background: #1f2937; }
.sb-chip, .sb-select, .sb-btn { background: #374151; border-color: #4b5563; color: #e5e7eb; }
.sb-chip-label, .sb-subtitle, .sb-control-group label, .sb-alert-time, .sb-alert-message { color: #9ca3af; }
.sb-alert-dismiss { background: #374151; color: #9ca3af; }
.sb-alert-dismiss:hover { background: #4b5563; color: #e5e7eb; }
.sb-empty h3 { color: #f3f4f6; }
.sb-btn-danger { background: #7f1d1d; color: #fecaca; border-color: #991b1b; }
}
`;
},
handleSave: null,
handleSaveApply: null,
handleReset: null
});