secubox-openwrt/package/secubox/luci-app-device-intel/htdocs/luci-static/resources/view/device-intel/dashboard.js
CyberMind-FR 7a9de56ba1 style(device-intel): Migrate dashboard views to KISS theme
- dashboard.js: KISS stats grid, source chips, type cards, recent devices table
- devices.js: KISS filter bar, device table with inline actions, edit/detail modals
- emulators.js: KISS emulator cards with status badges, mini tables
- mesh.js: KISS peer cards grid, remote devices table

Removes external CSS loading (cssLink pattern) and di-* class prefixes.
Uses KissTheme.E(), kiss-* classes, and CSS variables throughout.

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

196 lines
6.9 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.

'use strict';
'require view';
'require dom';
'require ui';
'require device-intel/api as api';
'require secubox/kiss-theme';
/**
* Device Intelligence Dashboard - KISS Style
* Copyright (C) 2025 CyberMind.fr
*/
return view.extend({
load: function() {
return Promise.all([
api.getSummary(),
api.getDevices(),
api.getDeviceTypes()
]);
},
render: function(data) {
var self = this;
var K = KissTheme;
var summary = data[0] || {};
var devResult = data[1] || {};
var typesResult = data[2] || {};
var devices = devResult.devices || [];
var types = typesResult.types || [];
// Count at-risk devices
var atRiskCount = devices.filter(function(d) {
return d.risk_score > 0;
}).length;
// Build type counts
var typeCounts = {};
devices.forEach(function(d) {
var t = d.device_type || 'unknown';
typeCounts[t] = (typeCounts[t] || 0) + 1;
});
var typeMap = {};
types.forEach(function(t) { typeMap[t.id] = t; });
// Build zone counts
var zoneCounts = {};
devices.forEach(function(d) {
var z = d.cg_zone || 'unzoned';
zoneCounts[z] = (zoneCounts[z] || 0) + 1;
});
// Recent devices (last 5 seen)
var recent = devices
.filter(function(d) { return d.last_seen; })
.sort(function(a, b) { return (b.last_seen || 0) - (a.last_seen || 0); })
.slice(0, 5);
var sources = summary.sources || {};
var emus = summary.emulators || {};
var content = K.E('div', {}, [
// Page Header
K.E('div', { 'style': 'display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;' }, [
K.E('div', {}, [
K.E('h2', { 'style': 'margin: 0; font-size: 24px; display: flex; align-items: center; gap: 10px;' }, [
K.E('span', {}, '🔍'),
'Device Intelligence'
]),
K.E('p', { 'style': 'margin: 4px 0 0; color: var(--kiss-muted, #94a3b8); font-size: 14px;' },
'Network device discovery and monitoring')
]),
K.E('button', {
'class': 'kiss-btn kiss-btn-blue',
'style': 'padding: 10px 16px; font-size: 14px;',
'click': function() {
api.refresh().then(function() {
window.location.href = window.location.pathname + '?' + Date.now();
});
}
}, '🔄 Refresh')
]),
// Stats Grid
K.E('div', { 'class': 'kiss-grid kiss-grid-4', 'style': 'gap: 16px; margin-bottom: 20px;' }, [
self.statCard(K, summary.total || 0, 'Total Devices', 'var(--kiss-blue, #3b82f6)', '📱'),
self.statCard(K, summary.online || 0, 'Online', 'var(--kiss-green, #22c55e)', '🟢'),
self.statCard(K, summary.mesh_peers || 0, 'Mesh Peers', 'var(--kiss-purple, #6366f1)', '🔗'),
self.statCard(K, atRiskCount, 'At Risk', 'var(--kiss-red, #ef4444)', '⚠️')
]),
// Data Sources Card
K.E('div', { 'class': 'kiss-card' }, [
K.E('div', { 'class': 'kiss-card-title' }, ['📡 ', 'Data Sources']),
K.E('div', { 'style': 'display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 16px;' }, [
self.sourceChip(K, 'MAC Guardian', sources.mac_guardian),
self.sourceChip(K, 'Client Guardian', sources.client_guardian),
self.sourceChip(K, 'DHCP Leases', sources.dhcp),
self.sourceChip(K, 'P2P Mesh', sources.p2p)
]),
K.E('div', { 'style': 'font-size: 13px; color: var(--kiss-muted); margin-bottom: 8px;' }, 'Emulators'),
K.E('div', { 'style': 'display: flex; flex-wrap: wrap; gap: 8px;' }, [
self.sourceChip(K, 'USB', emus.usb),
self.sourceChip(K, 'MQTT', emus.mqtt),
self.sourceChip(K, 'Zigbee', emus.zigbee)
])
]),
// Device Types Card
K.E('div', { 'class': 'kiss-card' }, [
K.E('div', { 'class': 'kiss-card-title' }, ['🏷 ', 'Device Types']),
K.E('div', { 'style': 'display: flex; flex-wrap: wrap; gap: 12px;' },
Object.keys(typeCounts).sort(function(a, b) {
return typeCounts[b] - typeCounts[a];
}).map(function(tid) {
var info = typeMap[tid] || { name: tid, color: '#6c757d' };
return K.E('div', {
'style': 'background: var(--kiss-bg2, #111827); border: 1px solid ' + (info.color || '#6c757d') + '; border-radius: 8px; padding: 12px 16px; text-align: center; min-width: 80px;'
}, [
K.E('div', { 'style': 'font-size: 24px; font-weight: bold; color: ' + (info.color || '#6c757d') }, String(typeCounts[tid])),
K.E('div', { 'style': 'font-size: 12px; color: var(--kiss-muted); margin-top: 4px;' }, info.name || tid)
]);
})
)
]),
// Zones Card
K.E('div', { 'class': 'kiss-card' }, [
K.E('div', { 'class': 'kiss-card-title' }, ['🗺 ', 'Zones']),
K.E('div', { 'style': 'display: flex; flex-wrap: wrap; gap: 8px;' },
Object.keys(zoneCounts).map(function(z) {
return K.E('span', {
'style': 'background: var(--kiss-purple, #6366f1); color: #fff; padding: 6px 12px; border-radius: 6px; font-size: 13px;'
}, z + ' (' + zoneCounts[z] + ')');
})
)
]),
// Recent Devices Card
K.E('div', { 'class': 'kiss-card' }, [
K.E('div', { 'class': 'kiss-card-title' }, ['🕐 ', 'Recent Devices']),
K.E('table', { 'class': 'kiss-table' }, [
K.E('thead', {}, K.E('tr', {}, [
K.E('th', {}, 'Device'),
K.E('th', {}, 'IP'),
K.E('th', {}, 'Type'),
K.E('th', {}, 'Vendor')
])),
K.E('tbody', {},
recent.length > 0
? recent.map(function(d) {
return K.E('tr', {}, [
K.E('td', {}, [
K.E('span', {
'style': 'display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 8px; background: ' + (d.online ? 'var(--kiss-green, #22c55e)' : 'var(--kiss-muted, #64748b)') + ';'
}),
d.label || d.hostname || d.mac
]),
K.E('td', {}, d.ip || '-'),
K.E('td', {}, d.device_type || '-'),
K.E('td', {}, d.vendor || '-')
]);
})
: [K.E('tr', {}, K.E('td', { 'colspan': '4', 'style': 'text-align: center; color: var(--kiss-muted); padding: 20px;' }, 'No recent devices'))]
)
])
])
]);
return KissTheme.wrap(content, 'admin/secubox/services/device-intel');
},
statCard: function(K, value, label, color, icon) {
return K.E('div', {
'style': 'background: var(--kiss-card, #161e2e); border: 1px solid var(--kiss-line, #1e293b); border-radius: 12px; padding: 20px; text-align: center;'
}, [
K.E('div', { 'style': 'font-size: 20px; margin-bottom: 8px;' }, icon),
K.E('div', { 'style': 'font-size: 32px; font-weight: bold; color: ' + color }, String(value)),
K.E('div', { 'style': 'font-size: 12px; color: var(--kiss-muted); margin-top: 4px;' }, label)
]);
},
sourceChip: function(K, name, active) {
return K.E('span', {
'style': 'padding: 6px 12px; border-radius: 6px; font-size: 13px; ' +
(active
? 'background: var(--kiss-green, #22c55e); color: #000;'
: 'background: var(--kiss-bg2, #111827); color: var(--kiss-muted); border: 1px solid var(--kiss-line, #1e293b);')
}, name);
},
handleSaveApply: null,
handleSave: null,
handleReset: null
});