From b3a02ffe14a053c2b5fac94883996bcb323d06af Mon Sep 17 00:00:00 2001 From: CyberMind-FR Date: Fri, 26 Dec 2025 08:47:07 +0100 Subject: [PATCH] feat(secubox): modernize dashboard with dynamic updates and module links MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Major dashboard improvements: โœจ New Features: - Auto-refresh every 30 seconds using poll API - Dynamic updates without page reload - Direct links to module dashboards (clickable cards) - Modern card-based layout with responsive grid - Stats overview with 4 key metrics - Progress bars instead of circular gauges - Better visual hierarchy and spacing ๐ŸŽจ Design Improvements: - Gradient header with version badge - Animated stat cards with hover effects - Color-coded health indicators - Pulsing status dots for running modules - Modern CSS with shadows and transitions - Responsive layout for mobile/tablet - Smooth animations and transitions ๐Ÿ”— Module Navigation: - Clickable module cards with direct links - Smart path mapping for all 14 modules - Visual running/stopped indicators - Hover effects for better UX ๐Ÿ“Š Better Data Display: - Horizontal progress bars for CPU/Memory/Disk - Real-time value updates - Compact module grid with icons - Alert system with severity colors ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- .../resources/secubox/dashboard.css | 407 +++++++++++++++++ .../resources/view/secubox/dashboard.js | 422 +++++++++++++----- 2 files changed, 705 insertions(+), 124 deletions(-) create mode 100644 luci-app-secubox/htdocs/luci-static/resources/secubox/dashboard.css diff --git a/luci-app-secubox/htdocs/luci-static/resources/secubox/dashboard.css b/luci-app-secubox/htdocs/luci-static/resources/secubox/dashboard.css new file mode 100644 index 00000000..afb525b9 --- /dev/null +++ b/luci-app-secubox/htdocs/luci-static/resources/secubox/dashboard.css @@ -0,0 +1,407 @@ +/* SecuBox Dashboard Styles */ + +.secubox-dashboard { + max-width: 1400px; + margin: 0 auto; + padding: 20px; +} + +/* Header */ +.secubox-header { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + border-radius: 12px; + padding: 24px; + margin-bottom: 24px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + color: white; +} + +.secubox-header-content { + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: 16px; +} + +.secubox-header h2 { + margin: 0 0 4px 0; + font-size: 28px; + font-weight: 700; +} + +.secubox-subtitle { + margin: 0; + opacity: 0.9; + font-size: 14px; +} + +.secubox-header-info { + display: flex; + gap: 12px; + flex-wrap: wrap; +} + +.secubox-badge { + background: rgba(255, 255, 255, 0.2); + padding: 6px 12px; + border-radius: 20px; + font-size: 13px; + font-weight: 500; + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.3); +} + +.secubox-badge-version { + background: rgba(255, 255, 255, 0.3); + font-weight: 600; +} + +/* Stats Grid */ +.secubox-stats-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 16px; + margin-bottom: 24px; +} + +.secubox-stat-card { + background: white; + border-radius: 12px; + padding: 20px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); + display: flex; + align-items: center; + gap: 16px; + transition: transform 0.2s, box-shadow 0.2s; +} + +.secubox-stat-card:hover { + transform: translateY(-4px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); +} + +.secubox-stat-icon { + font-size: 32px; + line-height: 1; +} + +.secubox-stat-content { + flex: 1; +} + +.secubox-stat-value { + font-size: 32px; + font-weight: 700; + line-height: 1; + margin-bottom: 4px; +} + +.secubox-stat-label { + font-size: 13px; + color: #64748b; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +/* Main Grid Layout */ +.secubox-main-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 24px; +} + +@media (max-width: 1024px) { + .secubox-main-grid { + grid-template-columns: 1fr; + } +} + +.secubox-column-left, +.secubox-column-right { + display: flex; + flex-direction: column; + gap: 24px; +} + +/* Card */ +.secubox-card { + background: white; + border-radius: 12px; + padding: 24px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); +} + +.secubox-card-title { + margin: 0 0 20px 0; + font-size: 18px; + font-weight: 600; + color: #1e293b; + border-bottom: 2px solid #f1f5f9; + padding-bottom: 12px; +} + +/* Progress Bars */ +.secubox-health-grid { + display: flex; + flex-direction: column; + gap: 20px; +} + +.secubox-progress-item { + display: flex; + flex-direction: column; + gap: 8px; +} + +.secubox-progress-header { + display: flex; + justify-content: space-between; + align-items: center; +} + +.secubox-progress-label { + font-weight: 600; + color: #1e293b; + font-size: 14px; +} + +.secubox-progress-value { + font-weight: 700; + font-size: 16px; +} + +.secubox-progress-bar { + height: 8px; + background: #f1f5f9; + border-radius: 4px; + overflow: hidden; +} + +.secubox-progress-fill { + height: 100%; + transition: width 0.6s ease, background 0.3s ease; + border-radius: 4px; +} + +.secubox-progress-details { + font-size: 12px; + color: #64748b; +} + +/* Active Modules */ +.secubox-modules-mini-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); + gap: 12px; +} + +.secubox-module-link { + text-decoration: none; + color: inherit; + transition: transform 0.2s; + display: block; +} + +.secubox-module-link:hover { + transform: translateY(-2px); +} + +.secubox-module-mini-card { + background: #f8fafc; + border-radius: 8px; + padding: 12px; + transition: all 0.2s; + cursor: pointer; +} + +.secubox-module-link:hover .secubox-module-mini-card { + background: #f1f5f9; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); +} + +.secubox-module-mini-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 8px; +} + +.secubox-module-mini-icon { + font-size: 24px; +} + +.secubox-status-dot { + width: 10px; + height: 10px; + border-radius: 50%; + display: inline-block; +} + +.secubox-status-dot.secubox-status-running { + background: #22c55e; + box-shadow: 0 0 8px rgba(34, 197, 94, 0.5); + animation: pulse 2s infinite; +} + +.secubox-status-dot.secubox-status-stopped { + background: #94a3b8; +} + +@keyframes pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.5; } +} + +.secubox-module-mini-body { + display: flex; + flex-direction: column; + gap: 4px; +} + +.secubox-module-mini-name { + font-weight: 600; + font-size: 13px; + color: #1e293b; + line-height: 1.3; +} + +.secubox-module-mini-status { + font-size: 11px; + color: #64748b; +} + +.secubox-module-running .secubox-module-mini-status { + color: #22c55e; +} + +/* Quick Actions */ +.secubox-actions-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); + gap: 12px; +} + +.secubox-action-btn { + background: white; + border: 2px solid #e2e8f0; + border-radius: 8px; + padding: 12px; + cursor: pointer; + transition: all 0.2s; + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + font-size: 13px; + font-weight: 500; +} + +.secubox-action-btn:hover { + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + border-color: currentColor; +} + +.secubox-action-icon { + font-size: 24px; +} + +.secubox-action-label { + color: #1e293b; +} + +/* Alerts */ +.secubox-alerts-list { + display: flex; + flex-direction: column; + gap: 8px; +} + +.secubox-alert { + padding: 12px 16px; + border-radius: 8px; + display: flex; + align-items: center; + gap: 12px; + font-size: 13px; + line-height: 1.5; +} + +.secubox-alert-success { + background: #f0fdf4; + border-left: 4px solid #22c55e; + color: #166534; +} + +.secubox-alert-info { + background: #eff6ff; + border-left: 4px solid #3b82f6; + color: #1e40af; +} + +.secubox-alert-warning { + background: #fffbeb; + border-left: 4px solid #f59e0b; + color: #92400e; +} + +.secubox-alert-error { + background: #fef2f2; + border-left: 4px solid #ef4444; + color: #991b1b; +} + +.secubox-alert-icon { + font-size: 18px; + flex-shrink: 0; +} + +.secubox-alert-content { + flex: 1; +} + +.secubox-view-all { + display: inline-block; + margin-top: 12px; + color: #6366f1; + text-decoration: none; + font-size: 13px; + font-weight: 500; + transition: color 0.2s; +} + +.secubox-view-all:hover { + color: #4f46e5; + text-decoration: underline; +} + +/* Empty State */ +.secubox-empty-state { + text-align: center; + padding: 40px 20px; + color: #94a3b8; + font-size: 14px; +} + +/* Animations */ +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.secubox-stat-card, +.secubox-card { + animation: fadeIn 0.5s ease-out; +} + +.secubox-stat-card:nth-child(1) { animation-delay: 0.1s; } +.secubox-stat-card:nth-child(2) { animation-delay: 0.2s; } +.secubox-stat-card:nth-child(3) { animation-delay: 0.3s; } +.secubox-stat-card:nth-child(4) { animation-delay: 0.4s; } diff --git a/luci-app-secubox/htdocs/luci-static/resources/view/secubox/dashboard.js b/luci-app-secubox/htdocs/luci-static/resources/view/secubox/dashboard.js index db9b3c47..36d63da3 100644 --- a/luci-app-secubox/htdocs/luci-static/resources/view/secubox/dashboard.js +++ b/luci-app-secubox/htdocs/luci-static/resources/view/secubox/dashboard.js @@ -3,137 +3,287 @@ 'require ui'; 'require secubox/api as API'; 'require dom'; +'require poll'; // Load CSS document.head.appendChild(E('link', { 'rel': 'stylesheet', 'type': 'text/css', - 'href': L.resource('secubox/secubox.css') + 'href': L.resource('secubox/dashboard.css') })); return view.extend({ + dashboardData: null, + healthData: null, + alertsData: null, + load: function() { + return this.refreshData(); + }, + + refreshData: function() { + var self = this; return Promise.all([ API.getDashboardData(), API.getSystemHealth(), API.getAlerts() - ]); + ]).then(function(data) { + self.dashboardData = data[0] || {}; + self.healthData = data[1] || {}; + self.alertsData = data[2] || {}; + return data; + }); }, render: function(data) { - var dashboard = data[0] || {}; - var health = data[1] || {}; - var alertsData = data[2] || {}; + var self = this; + var container = E('div', { 'class': 'secubox-dashboard' }); - var status = dashboard.status || {}; - var modules = dashboard.modules || []; - var counts = dashboard.counts || {}; - var alerts = alertsData.alerts || []; + // Header with version + var status = this.dashboardData.status || {}; + container.appendChild(this.renderHeader(status)); - var container = E('div', { 'class': 'cbi-map' }); + // Stats Overview Cards + container.appendChild(this.renderStatsOverview()); - // Header - container.appendChild(E('h2', {}, 'SecuBox Central Hub')); - container.appendChild(E('p', {}, - 'Hostname: ' + (status.hostname || 'Unknown') + ' | ' + - 'Uptime: ' + API.formatUptime(status.uptime) + ' | ' + - 'Load: ' + (status.load || '0.00') - )); + // Main Content Grid + var mainGrid = E('div', { 'class': 'secubox-main-grid' }, [ + // Left column + E('div', { 'class': 'secubox-column-left' }, [ + this.renderSystemHealth(this.healthData), + this.renderAlerts(this.alertsData.alerts || []) + ]), + // Right column + E('div', { 'class': 'secubox-column-right' }, [ + this.renderActiveModules(), + this.renderQuickActions() + ]) + ]); - // System Health Section - container.appendChild(this.renderSystemHealth(health)); + container.appendChild(mainGrid); - // Quick Actions Section - container.appendChild(this.renderQuickActions()); - - // Alerts Section - if (alerts.length > 0) { - container.appendChild(this.renderAlerts(alerts)); - } - - // Modules Grid - container.appendChild(this.renderModulesGrid(modules)); + // Start auto-refresh poll + poll.add(function() { + return self.refreshData().then(function() { + self.updateDynamicElements(); + }); + }, 30); // Refresh every 30 seconds return container; }, + renderHeader: function(status) { + return E('div', { 'class': 'secubox-header' }, [ + E('div', { 'class': 'secubox-header-content' }, [ + E('div', {}, [ + E('h2', {}, '๐Ÿ›ก๏ธ SecuBox Central Hub'), + E('p', { 'class': 'secubox-subtitle' }, 'Security & Network Management Suite') + ]), + E('div', { 'class': 'secubox-header-info' }, [ + E('span', { 'class': 'secubox-badge secubox-badge-version' }, + 'v' + (status.version || '0.0.1-beta')), + E('span', { 'class': 'secubox-badge' }, + 'โฑ๏ธ ' + API.formatUptime(status.uptime)), + E('span', { 'class': 'secubox-badge', 'id': 'secubox-hostname' }, + '๐Ÿ–ฅ๏ธ ' + (status.hostname || 'SecuBox')) + ]) + ]) + ]); + }, + + renderStatsOverview: function() { + var modules = this.dashboardData.modules || []; + var counts = this.dashboardData.counts || {}; + + var stats = [ + { + label: 'Total Modules', + value: counts.total || modules.length, + icon: '๐Ÿ“ฆ', + color: '#6366f1', + id: 'stat-total' + }, + { + label: 'Installed', + value: counts.installed || 0, + icon: 'โœ“', + color: '#22c55e', + id: 'stat-installed' + }, + { + label: 'Running', + value: counts.running || 0, + icon: 'โ–ถ', + color: '#00ab44', + id: 'stat-running' + }, + { + label: 'Alerts', + value: (this.alertsData.alerts || []).length, + icon: 'โš ๏ธ', + color: '#f59e0b', + id: 'stat-alerts' + } + ]; + + return E('div', { 'class': 'secubox-stats-grid' }, + stats.map(function(stat) { + return E('div', { + 'class': 'secubox-stat-card', + 'style': 'border-top: 3px solid ' + stat.color + }, [ + E('div', { 'class': 'secubox-stat-icon' }, stat.icon), + E('div', { 'class': 'secubox-stat-content' }, [ + E('div', { + 'class': 'secubox-stat-value', + 'id': stat.id, + 'style': 'color: ' + stat.color + }, stat.value), + E('div', { 'class': 'secubox-stat-label' }, stat.label) + ]) + ]); + }) + ); + }, + renderSystemHealth: function(health) { var cpu = health.cpu || {}; var memory = health.memory || {}; var disk = health.disk || {}; - var network = health.network || {}; - var section = E('div', { 'class': 'secubox-health-section' }, [ - E('h3', {}, 'System Health'), + return E('div', { 'class': 'secubox-card' }, [ + E('h3', { 'class': 'secubox-card-title' }, '๐Ÿ“Š System Health'), E('div', { 'class': 'secubox-health-grid' }, [ - this.renderGauge('CPU', cpu.percent || 0, '%', cpu.load_1min), - this.renderGauge('Memory', memory.percent || 0, '%', - API.formatBytes(memory.used_kb * 1024) + ' / ' + API.formatBytes(memory.total_kb * 1024)), - this.renderGauge('Disk', disk.percent || 0, '%', - API.formatBytes(disk.used_kb * 1024) + ' / ' + API.formatBytes(disk.total_kb * 1024)), - this.renderGauge('Network', 0, '', - 'RX: ' + API.formatBytes(network.rx_bytes) + ' | TX: ' + API.formatBytes(network.tx_bytes)) + this.renderProgressBar('CPU', cpu.percent || 0, cpu.load_1min || '0.00', 'cpu'), + this.renderProgressBar('Memory', memory.percent || 0, + API.formatBytes((memory.used_kb || 0) * 1024) + ' / ' + + API.formatBytes((memory.total_kb || 0) * 1024), 'memory'), + this.renderProgressBar('Disk', disk.percent || 0, + API.formatBytes((disk.used_kb || 0) * 1024) + ' / ' + + API.formatBytes((disk.total_kb || 0) * 1024), 'disk') ]) ]); - - return section; }, - renderGauge: function(label, percent, unit, details) { + renderProgressBar: function(label, percent, details, id) { var color = percent < 70 ? '#22c55e' : percent < 85 ? '#f59e0b' : '#ef4444'; - return E('div', { 'class': 'secubox-gauge' }, [ - E('div', { 'class': 'secubox-gauge-label' }, label), - E('div', { 'class': 'secubox-gauge-chart' }, [ - E('svg', { 'width': '120', 'height': '120', 'viewBox': '0 0 120 120' }, [ - E('circle', { - 'cx': '60', 'cy': '60', 'r': '54', - 'fill': 'none', 'stroke': '#1e293b', 'stroke-width': '12' - }), - E('circle', { - 'cx': '60', 'cy': '60', 'r': '54', - 'fill': 'none', 'stroke': color, 'stroke-width': '12', - 'stroke-dasharray': (339.292 * percent / 100) + ' 339.292', - 'stroke-linecap': 'round', - 'transform': 'rotate(-90 60 60)' - }), - E('text', { - 'x': '60', 'y': '65', 'text-anchor': 'middle', - 'font-size': '24', 'font-weight': 'bold', 'fill': color - }, percent + unit) - ]) + return E('div', { 'class': 'secubox-progress-item' }, [ + E('div', { 'class': 'secubox-progress-header' }, [ + E('span', { 'class': 'secubox-progress-label' }, label), + E('span', { + 'class': 'secubox-progress-value', + 'id': 'health-' + id + '-percent', + 'style': 'color: ' + color + }, percent + '%') ]), - E('div', { 'class': 'secubox-gauge-details' }, details || '') + E('div', { 'class': 'secubox-progress-bar' }, [ + E('div', { + 'class': 'secubox-progress-fill', + 'id': 'health-' + id + '-bar', + 'style': 'width: ' + percent + '%; background: ' + color + }) + ]), + E('div', { + 'class': 'secubox-progress-details', + 'id': 'health-' + id + '-details' + }, details) + ]); + }, + + renderActiveModules: function() { + var modules = this.dashboardData.modules || []; + var activeModules = modules.filter(function(m) { return m.installed; }); + + // Map module IDs to their dashboard paths + var modulePaths = { + 'crowdsec': 'admin/secubox/security/crowdsec', + 'netdata': 'admin/secubox/monitoring/netdata', + 'netifyd': 'admin/secubox/security/netifyd', + 'wireguard': 'admin/secubox/network/wireguard', + 'network_modes': 'admin/secubox/network/modes', + 'client_guardian': 'admin/secubox/security/guardian', + 'system_hub': 'admin/secubox/system/hub', + 'bandwidth_manager': 'admin/secubox/network/bandwidth', + 'auth_guardian': 'admin/secubox/security/auth', + 'media_flow': 'admin/secubox/network/media', + 'vhost_manager': 'admin/secubox/system/vhost', + 'traffic_shaper': 'admin/secubox/network/shaper', + 'cdn_cache': 'admin/secubox/network/cdn', + 'ksm_manager': 'admin/secubox/security/ksm' + }; + + var moduleCards = activeModules.map(function(module) { + var isRunning = module.running; + var statusClass = isRunning ? 'running' : 'stopped'; + var dashboardPath = modulePaths[module.id] || ('admin/secubox/' + module.id); + + return E('a', { + 'href': L.url(dashboardPath), + 'class': 'secubox-module-link secubox-module-' + statusClass + }, [ + E('div', { + 'class': 'secubox-module-mini-card', + 'style': 'border-left: 4px solid ' + (module.color || '#64748b') + }, [ + E('div', { 'class': 'secubox-module-mini-header' }, [ + E('span', { 'class': 'secubox-module-mini-icon' }, module.icon || '๐Ÿ“ฆ'), + E('span', { + 'class': 'secubox-status-dot secubox-status-' + statusClass, + 'title': isRunning ? 'Running' : 'Stopped' + }) + ]), + E('div', { 'class': 'secubox-module-mini-body' }, [ + E('div', { 'class': 'secubox-module-mini-name' }, module.name || module.id), + E('div', { 'class': 'secubox-module-mini-status' }, + isRunning ? 'โ— Running' : 'โ—‹ Stopped') + ]) + ]) + ]); + }); + + return E('div', { 'class': 'secubox-card' }, [ + E('h3', { 'class': 'secubox-card-title' }, '๐ŸŽฏ Active Modules (' + activeModules.length + ')'), + E('div', { 'class': 'secubox-modules-mini-grid' }, + moduleCards.length > 0 ? moduleCards : [ + E('p', { 'class': 'secubox-empty-state' }, 'No modules installed') + ] + ) ]); }, renderQuickActions: function() { var self = this; var actions = [ - { name: 'restart_rpcd', label: 'Restart RPCD', icon: '๐Ÿ”„' }, - { name: 'restart_uhttpd', label: 'Restart uHTTPd', icon: '๐ŸŒ' }, - { name: 'clear_cache', label: 'Clear Cache', icon: '๐Ÿงน' }, - { name: 'backup_config', label: 'Backup Config', icon: '๐Ÿ’พ' }, - { name: 'restart_network', label: 'Restart Network', icon: '๐Ÿ“ก' }, - { name: 'restart_firewall', label: 'Restart Firewall', icon: '๐Ÿ›ก๏ธ' } + { name: 'restart_rpcd', label: 'RPCD', icon: '๐Ÿ”„', color: '#6366f1' }, + { name: 'restart_uhttpd', label: 'Web Server', icon: '๐ŸŒ', color: '#00ab44' }, + { name: 'restart_network', label: 'Network', icon: '๐Ÿ“ก', color: '#06b6d4' }, + { name: 'restart_firewall', label: 'Firewall', icon: '๐Ÿ›ก๏ธ', color: '#ef4444' }, + { name: 'clear_cache', label: 'Clear Cache', icon: '๐Ÿงน', color: '#f59e0b' }, + { name: 'backup_config', label: 'Backup', icon: '๐Ÿ’พ', color: '#8b5cf6' } ]; var buttons = actions.map(function(action) { return E('button', { - 'class': 'cbi-button cbi-button-action', + 'class': 'secubox-action-btn', + 'style': 'border-color: ' + action.color, 'click': function() { self.executeQuickAction(action.name, action.label); } - }, action.icon + ' ' + action.label); + }, [ + E('span', { 'class': 'secubox-action-icon' }, action.icon), + E('span', { 'class': 'secubox-action-label' }, action.label) + ]); }); - return E('div', { 'class': 'secubox-quick-actions' }, [ - E('h3', {}, 'Quick Actions'), + return E('div', { 'class': 'secubox-card' }, [ + E('h3', { 'class': 'secubox-card-title' }, 'โšก Quick Actions'), E('div', { 'class': 'secubox-actions-grid' }, buttons) ]); }, executeQuickAction: function(action, label) { + var self = this; ui.showModal(_('Quick Action'), [ E('p', { 'class': 'spinning' }, _('Executing: ') + label + '...') ]); @@ -141,9 +291,15 @@ return view.extend({ API.quickAction(action).then(function(result) { ui.hideModal(); if (result && result.success) { - ui.addNotification(null, E('p', result.message || 'Action completed'), 'info'); + ui.addNotification(null, E('p', 'โœ“ ' + (result.message || 'Action completed')), 'info'); + // Refresh data after action + setTimeout(function() { + self.refreshData().then(function() { + self.updateDynamicElements(); + }); + }, 2000); } else { - ui.addNotification(null, E('p', result.message || 'Action failed'), 'error'); + ui.addNotification(null, E('p', 'โœ— ' + (result.message || 'Action failed')), 'error'); } }).catch(function(err) { ui.hideModal(); @@ -152,64 +308,82 @@ return view.extend({ }, renderAlerts: function(alerts) { - var alertItems = alerts.map(function(alert) { - var severityClass = 'secubox-alert-' + (alert.severity || 'info'); - return E('div', { 'class': 'secubox-alert ' + severityClass }, [ - E('strong', {}, alert.module + ': '), - E('span', {}, alert.message) + if (!alerts || alerts.length === 0) { + return E('div', { 'class': 'secubox-card' }, [ + E('h3', { 'class': 'secubox-card-title' }, 'โœ“ System Status'), + E('div', { 'class': 'secubox-alert secubox-alert-success' }, [ + E('span', {}, 'โœ“ All systems operational') + ]) ]); - }); + } - return E('div', { 'class': 'secubox-alerts-section' }, [ - E('h3', {}, 'Recent Alerts (' + alerts.length + ')'), - E('div', { 'class': 'secubox-alerts-list' }, alertItems) - ]); - }, - - renderModulesGrid: function(modules) { - var moduleCards = modules.map(function(module) { - var statusClass = module.installed ? (module.running ? 'running' : 'stopped') : 'not-installed'; - var statusIcon = module.installed ? (module.running ? 'โœ“' : 'โœ—') : 'โ—‹'; - var statusColor = module.installed ? (module.running ? '#22c55e' : '#ef4444') : '#64748b'; - - return E('div', { - 'class': 'secubox-module-card', - 'data-status': statusClass - }, [ - E('div', { 'class': 'secubox-module-header' }, [ - E('div', { - 'class': 'secubox-module-icon', - 'style': 'background-color: ' + (module.color || '#64748b') - }, module.icon || '๐Ÿ“ฆ'), - E('div', { - 'class': 'secubox-module-status', - 'style': 'color: ' + statusColor - }, statusIcon) - ]), - E('div', { 'class': 'secubox-module-body' }, [ - E('div', { 'class': 'secubox-module-name' }, module.name || module.id), - E('div', { 'class': 'secubox-module-description' }, module.description || ''), - E('div', { 'class': 'secubox-module-category' }, module.category || 'other') - ]), - E('div', { 'class': 'secubox-module-footer' }, [ - module.installed ? E('a', { - 'href': '#', - 'class': 'cbi-button cbi-button-link', - 'click': function(ev) { - ev.preventDefault(); - window.location.hash = '#admin/secubox/' + module.id; - } - }, 'Open Dashboard') : E('span', { 'class': 'secubox-not-installed' }, 'Not Installed') + var alertItems = alerts.slice(0, 5).map(function(alert) { + var severityClass = 'secubox-alert-' + (alert.severity || 'info'); + var severityIcon = alert.severity === 'error' ? 'โœ—' : + alert.severity === 'warning' ? 'โš ๏ธ' : 'โ„น๏ธ'; + return E('div', { 'class': 'secubox-alert ' + severityClass }, [ + E('span', { 'class': 'secubox-alert-icon' }, severityIcon), + E('div', { 'class': 'secubox-alert-content' }, [ + E('strong', {}, alert.module || 'System'), + E('span', {}, ': ' + alert.message) ]) ]); }); - return E('div', { 'class': 'secubox-modules-section' }, [ - E('h3', {}, 'Installed Modules'), - E('div', { 'class': 'secubox-modules-grid' }, moduleCards) + return E('div', { 'class': 'secubox-card' }, [ + E('h3', { 'class': 'secubox-card-title' }, + 'โš ๏ธ Alerts (' + alerts.length + ')'), + E('div', { 'class': 'secubox-alerts-list' }, alertItems), + alerts.length > 5 ? E('a', { + 'href': '#', + 'class': 'secubox-view-all', + 'click': function(ev) { + ev.preventDefault(); + window.location.hash = '#admin/secubox/alerts'; + } + }, 'View all alerts โ†’') : null ]); }, + updateDynamicElements: function() { + // Update stats + var counts = this.dashboardData.counts || {}; + var totalEl = document.getElementById('stat-total'); + var installedEl = document.getElementById('stat-installed'); + var runningEl = document.getElementById('stat-running'); + var alertsEl = document.getElementById('stat-alerts'); + + if (totalEl) totalEl.textContent = counts.total || 0; + if (installedEl) installedEl.textContent = counts.installed || 0; + if (runningEl) runningEl.textContent = counts.running || 0; + if (alertsEl) alertsEl.textContent = (this.alertsData.alerts || []).length; + + // Update health bars + var health = this.healthData; + this.updateHealthBar('cpu', health.cpu); + this.updateHealthBar('memory', health.memory); + this.updateHealthBar('disk', health.disk); + }, + + updateHealthBar: function(type, data) { + if (!data) return; + + var percent = data.percent || 0; + var color = percent < 70 ? '#22c55e' : percent < 85 ? '#f59e0b' : '#ef4444'; + + var percentEl = document.getElementById('health-' + type + '-percent'); + var barEl = document.getElementById('health-' + type + '-bar'); + + if (percentEl) { + percentEl.textContent = percent + '%'; + percentEl.style.color = color; + } + if (barEl) { + barEl.style.width = percent + '%'; + barEl.style.background = color; + } + }, + handleSaveApply: null, handleSave: null, handleReset: null