fix(luci): Device Intel emulators dark theme
- Remove KissTheme dependency, use direct RPC calls - Dark theme colors (#12121a, #1a1a24, #00d4aa, #00a0ff) - Update common.css with matching dark styles - Simplified DOM rendering with inline styles Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
62f89f0d57
commit
baa07a7521
@ -1,4 +1,4 @@
|
||||
/* SecuBox Device Intelligence — Dashboard Styles */
|
||||
/* SecuBox Device Intelligence — Dark Theme Styles */
|
||||
|
||||
.di-stats {
|
||||
display: flex;
|
||||
@ -12,8 +12,8 @@
|
||||
min-width: 140px;
|
||||
padding: 1em;
|
||||
border-radius: 8px;
|
||||
background: var(--bg-alt, #f8f9fa);
|
||||
border: 1px solid var(--border-color, #dee2e6);
|
||||
background: #12121a;
|
||||
border: 1px solid rgba(255,255,255,0.05);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@ -21,11 +21,12 @@
|
||||
font-size: 2em;
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
color: #00d4aa;
|
||||
}
|
||||
|
||||
.di-stat-card .label {
|
||||
font-size: 0.85em;
|
||||
color: var(--text-muted, #6c757d);
|
||||
color: #808090;
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
|
||||
@ -39,8 +40,8 @@
|
||||
.di-type-card {
|
||||
padding: 0.75em 1em;
|
||||
border-radius: 6px;
|
||||
border-left: 4px solid var(--type-color, #6c757d);
|
||||
background: var(--bg-alt, #f8f9fa);
|
||||
border-left: 4px solid #00d4aa;
|
||||
background: #1a1a24;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
@ -49,11 +50,12 @@
|
||||
.di-type-card .count {
|
||||
font-size: 1.5em;
|
||||
font-weight: 700;
|
||||
color: #00d4aa;
|
||||
}
|
||||
|
||||
.di-type-card .name {
|
||||
font-size: 0.85em;
|
||||
color: var(--text-muted, #6c757d);
|
||||
color: #808090;
|
||||
}
|
||||
|
||||
.di-source-bar {
|
||||
@ -67,18 +69,18 @@
|
||||
padding: 0.25em 0.75em;
|
||||
border-radius: 12px;
|
||||
font-size: 0.8em;
|
||||
background: var(--bg-alt, #e9ecef);
|
||||
background: #1a1a24;
|
||||
color: #808090;
|
||||
}
|
||||
|
||||
.di-source-chip.active {
|
||||
background: #22c55e;
|
||||
color: #fff;
|
||||
background: rgba(0,212,170,0.2);
|
||||
color: #00d4aa;
|
||||
}
|
||||
|
||||
.di-source-chip.inactive {
|
||||
background: #ef4444;
|
||||
color: #fff;
|
||||
opacity: 0.7;
|
||||
background: rgba(255,77,77,0.2);
|
||||
color: #ff4d4d;
|
||||
}
|
||||
|
||||
.di-device-table {
|
||||
@ -89,19 +91,21 @@
|
||||
.di-device-table th {
|
||||
text-align: left;
|
||||
padding: 0.5em 0.75em;
|
||||
border-bottom: 2px solid var(--border-color, #dee2e6);
|
||||
border-bottom: 1px solid #2a2a3a;
|
||||
font-size: 0.85em;
|
||||
color: var(--text-muted, #6c757d);
|
||||
color: #808090;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.di-device-table td {
|
||||
padding: 0.5em 0.75em;
|
||||
border-bottom: 1px solid var(--border-color-light, #f0f0f0);
|
||||
border-bottom: 1px solid #1a1a24;
|
||||
font-size: 0.9em;
|
||||
color: #e0e0e0;
|
||||
}
|
||||
|
||||
.di-device-table tr:hover {
|
||||
background: var(--bg-hover, #f8f9fa);
|
||||
background: rgba(255,255,255,0.02);
|
||||
}
|
||||
|
||||
.di-online-dot {
|
||||
@ -112,14 +116,14 @@
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
.di-online-dot.online { background: #22c55e; }
|
||||
.di-online-dot.offline { background: #ef4444; }
|
||||
.di-online-dot.online { background: #00d4aa; box-shadow: 0 0 6px #00d4aa; }
|
||||
.di-online-dot.offline { background: #ff4d4d; }
|
||||
|
||||
.di-emu-card {
|
||||
padding: 1em;
|
||||
border-radius: 8px;
|
||||
background: var(--bg-alt, #f8f9fa);
|
||||
border: 1px solid var(--border-color, #dee2e6);
|
||||
background: #12121a;
|
||||
border: 1px solid rgba(255,255,255,0.05);
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
@ -128,6 +132,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.di-emu-card .status {
|
||||
@ -137,13 +142,13 @@
|
||||
}
|
||||
|
||||
.di-emu-card .status.enabled {
|
||||
background: #dcfce7;
|
||||
color: #166534;
|
||||
background: rgba(0,212,170,0.2);
|
||||
color: #00d4aa;
|
||||
}
|
||||
|
||||
.di-emu-card .status.disabled {
|
||||
background: #fee2e2;
|
||||
color: #991b1b;
|
||||
background: rgba(128,128,144,0.2);
|
||||
color: #808090;
|
||||
}
|
||||
|
||||
.di-filter-bar {
|
||||
@ -157,7 +162,15 @@
|
||||
.di-filter-bar input,
|
||||
.di-filter-bar select {
|
||||
padding: 0.4em 0.6em;
|
||||
border: 1px solid var(--border-color, #dee2e6);
|
||||
border: 1px solid #2a2a3a;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9em;
|
||||
background: #1a1a24;
|
||||
color: #e0e0e0;
|
||||
}
|
||||
|
||||
.di-filter-bar input:focus,
|
||||
.di-filter-bar select:focus {
|
||||
outline: none;
|
||||
border-color: #00a0ff;
|
||||
}
|
||||
|
||||
@ -2,30 +2,109 @@
|
||||
'require view';
|
||||
'require dom';
|
||||
'require ui';
|
||||
'require device-intel/api as api';
|
||||
'require secubox/kiss-theme';
|
||||
'require rpc';
|
||||
|
||||
/**
|
||||
* Device Intel - Emulator Modules - KISS Style
|
||||
* Copyright (C) 2025 CyberMind.fr
|
||||
*/
|
||||
var callGetEmulators = rpc.declare({
|
||||
object: 'luci.device-intel',
|
||||
method: 'get_emulators',
|
||||
expect: {}
|
||||
});
|
||||
|
||||
var callGetDevices = rpc.declare({
|
||||
object: 'luci.device-intel',
|
||||
method: 'get_devices',
|
||||
expect: {}
|
||||
});
|
||||
|
||||
function createCard(title, icon, enabled, content, borderColor) {
|
||||
return E('div', {
|
||||
'style': 'background:#12121a;border-radius:8px;padding:16px;margin-bottom:16px;' +
|
||||
'border-left:4px solid ' + (borderColor || '#2a2a3a') + ';'
|
||||
}, [
|
||||
E('div', { 'style': 'display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;' }, [
|
||||
E('div', { 'style': 'display:flex;align-items:center;gap:8px;' }, [
|
||||
E('span', { 'style': 'font-size:1.2rem;' }, icon),
|
||||
E('span', { 'style': 'font-size:1rem;font-weight:600;color:#fff;' }, title)
|
||||
]),
|
||||
E('span', {
|
||||
'style': 'padding:4px 12px;border-radius:6px;font-size:11px;font-weight:600;' +
|
||||
(enabled ? 'background:rgba(0,212,170,0.2);color:#00d4aa;' : 'background:rgba(128,128,144,0.2);color:#808090;')
|
||||
}, enabled ? 'Enabled' : 'Disabled')
|
||||
]),
|
||||
E('div', {}, content)
|
||||
]);
|
||||
}
|
||||
|
||||
function createMetric(label, value, color) {
|
||||
return E('span', { 'style': 'margin-right:20px;' }, [
|
||||
E('span', { 'style': 'color:#808090;' }, label + ': '),
|
||||
E('strong', { 'style': 'color:' + (color || '#00d4aa') + ';' }, String(value))
|
||||
]);
|
||||
}
|
||||
|
||||
function statusDot(running, label) {
|
||||
var color = running ? '#00d4aa' : '#ff4d4d';
|
||||
return E('span', { 'style': 'display:inline-flex;align-items:center;gap:4px;' }, [
|
||||
E('span', {
|
||||
'style': 'width:8px;height:8px;border-radius:50%;background:' + color + ';'
|
||||
}),
|
||||
E('span', { 'style': 'color:' + color + ';' }, label)
|
||||
]);
|
||||
}
|
||||
|
||||
function deviceTable(devices, fields) {
|
||||
if (devices.length === 0) {
|
||||
return E('p', { 'style': 'color:#808090;font-style:italic;text-align:center;padding:16px;margin:0;' },
|
||||
'No devices discovered from this emulator.');
|
||||
}
|
||||
|
||||
var headers = {
|
||||
'hostname': 'Name',
|
||||
'vendor': 'Model/Vendor',
|
||||
'device_type': 'Type',
|
||||
'mac': 'ID',
|
||||
'ip': 'IP'
|
||||
};
|
||||
|
||||
return E('div', { 'style': 'overflow-x:auto;' }, [
|
||||
E('table', { 'style': 'width:100%;border-collapse:collapse;font-size:0.85rem;' }, [
|
||||
E('thead', {}, [
|
||||
E('tr', { 'style': 'border-bottom:1px solid #2a2a3a;' },
|
||||
fields.map(function(f) {
|
||||
return E('th', { 'style': 'padding:8px;text-align:left;color:#808090;font-weight:600;' }, headers[f] || f);
|
||||
})
|
||||
)
|
||||
]),
|
||||
E('tbody', {},
|
||||
devices.slice(0, 20).map(function(d) {
|
||||
return E('tr', { 'style': 'border-bottom:1px solid #1a1a24;' },
|
||||
fields.map(function(f) {
|
||||
var val = d[f] || '-';
|
||||
var style = 'padding:8px;color:#e0e0e0;';
|
||||
if (f === 'hostname') style = 'padding:8px;color:#00a0ff;font-weight:500;';
|
||||
if (f === 'device_type') style = 'padding:8px;color:#00d4aa;';
|
||||
return E('td', { 'style': style }, String(val));
|
||||
})
|
||||
);
|
||||
})
|
||||
)
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
return view.extend({
|
||||
load: function() {
|
||||
return Promise.all([
|
||||
api.getEmulators(),
|
||||
api.getDevices()
|
||||
callGetEmulators().catch(function() { return {}; }),
|
||||
callGetDevices().catch(function() { return { devices: [] }; })
|
||||
]);
|
||||
},
|
||||
|
||||
render: function(data) {
|
||||
var self = this;
|
||||
var K = KissTheme;
|
||||
var emuResult = data[0] || {};
|
||||
var devResult = data[1] || {};
|
||||
var devices = devResult.devices || [];
|
||||
|
||||
// Filter devices by emulator source
|
||||
var usbDevices = devices.filter(function(d) { return d.emulator_source === 'usb'; });
|
||||
var mqttDevices = devices.filter(function(d) { return d.emulator_source === 'mqtt'; });
|
||||
var zigbeeDevices = devices.filter(function(d) { return d.emulator_source === 'zigbee'; });
|
||||
@ -34,109 +113,57 @@ return view.extend({
|
||||
var mqtt = emuResult.mqtt || {};
|
||||
var zigbee = emuResult.zigbee || {};
|
||||
|
||||
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', {}, '🔌'),
|
||||
'Emulator Modules'
|
||||
]),
|
||||
K.E('p', { 'style': 'margin: 4px 0 0; color: var(--kiss-muted, #94a3b8); font-size: 14px;' },
|
||||
var view = E('div', { 'style': 'max-width:1200px;margin:0 auto;padding:20px;' }, [
|
||||
// Header
|
||||
E('div', { 'style': 'display:flex;justify-content:space-between;align-items:center;margin-bottom:20px;' }, [
|
||||
E('div', {}, [
|
||||
E('h2', {
|
||||
'style': 'margin:0;font-size:1.5rem;background:linear-gradient(90deg,#00d4aa,#00a0ff);' +
|
||||
'-webkit-background-clip:text;-webkit-text-fill-color:transparent;'
|
||||
}, 'Emulator Modules'),
|
||||
E('p', { 'style': 'margin:4px 0 0;color:#808090;font-size:14px;' },
|
||||
'Pluggable device discovery modules for USB, MQTT, and Zigbee peripherals')
|
||||
]),
|
||||
K.E('a', {
|
||||
E('a', {
|
||||
'href': L.url('admin/secubox/device-intel/settings'),
|
||||
'class': 'kiss-btn kiss-btn-blue',
|
||||
'style': 'padding: 10px 16px; text-decoration: none;'
|
||||
}, '⚙️ Configure')
|
||||
'style': 'padding:8px 16px;border-radius:6px;text-decoration:none;font-size:13px;' +
|
||||
'background:rgba(0,160,255,0.2);color:#00a0ff;border:1px solid rgba(0,160,255,0.3);'
|
||||
}, 'Configure')
|
||||
]),
|
||||
|
||||
// USB Card
|
||||
self.emuCard(K, '🔌 USB Peripherals', usb.enabled, [
|
||||
K.E('div', { 'style': 'display: flex; gap: 24px; margin-bottom: 16px; flex-wrap: wrap;' }, [
|
||||
K.E('span', {}, ['System USB devices: ', K.E('strong', {}, String(usb.device_count || 0))]),
|
||||
K.E('span', {}, ['Discovered: ', K.E('strong', { 'style': 'color: var(--kiss-green);' }, String(usbDevices.length))])
|
||||
createCard('USB Peripherals', '🔌', usb.enabled, [
|
||||
E('div', { 'style': 'display:flex;flex-wrap:wrap;gap:8px;margin-bottom:12px;' }, [
|
||||
createMetric('System USB devices', usb.device_count || 0, '#00a0ff'),
|
||||
createMetric('Discovered', usbDevices.length, '#00d4aa')
|
||||
]),
|
||||
self.deviceMiniTable(K, usbDevices, ['hostname', 'vendor', 'device_type'])
|
||||
]),
|
||||
deviceTable(usbDevices, ['hostname', 'vendor', 'device_type'])
|
||||
], usb.enabled ? '#00d4aa' : '#808090'),
|
||||
|
||||
// MQTT Card
|
||||
self.emuCard(K, '📡 MQTT Broker', mqtt.enabled, [
|
||||
K.E('div', { 'style': 'display: flex; gap: 24px; margin-bottom: 16px; flex-wrap: wrap;' }, [
|
||||
K.E('span', {}, 'Broker: ' + (mqtt.broker_host || '127.0.0.1') + ':' + (mqtt.broker_port || 1883)),
|
||||
K.E('span', {}, [
|
||||
'Status: ',
|
||||
mqtt.broker_running
|
||||
? K.E('span', { 'style': 'color: var(--kiss-green, #22c55e);' }, '● Running')
|
||||
: K.E('span', { 'style': 'color: var(--kiss-red, #ef4444);' }, '● Not Found')
|
||||
]),
|
||||
K.E('span', {}, ['Clients discovered: ', K.E('strong', { 'style': 'color: var(--kiss-green);' }, String(mqttDevices.length))])
|
||||
createCard('MQTT Broker', '📡', mqtt.enabled, [
|
||||
E('div', { 'style': 'display:flex;flex-wrap:wrap;gap:16px;margin-bottom:12px;align-items:center;' }, [
|
||||
E('span', { 'style': 'color:#e0e0e0;' },
|
||||
'Broker: ' + (mqtt.broker_host || '127.0.0.1') + ':' + (mqtt.broker_port || 1883)),
|
||||
statusDot(mqtt.broker_running, mqtt.broker_running ? 'Running' : 'Not Found'),
|
||||
createMetric('Clients discovered', mqttDevices.length, '#00d4aa')
|
||||
]),
|
||||
self.deviceMiniTable(K, mqttDevices, ['hostname', 'vendor', 'device_type'])
|
||||
]),
|
||||
deviceTable(mqttDevices, ['hostname', 'vendor', 'device_type'])
|
||||
], mqtt.enabled ? '#00a0ff' : '#808090'),
|
||||
|
||||
// Zigbee Card
|
||||
self.emuCard(K, '📻 Zigbee Coordinator', zigbee.enabled, [
|
||||
K.E('div', { 'style': 'display: flex; gap: 24px; margin-bottom: 16px; flex-wrap: wrap;' }, [
|
||||
K.E('span', {}, 'Adapter: ' + (zigbee.adapter || 'zigbee2mqtt')),
|
||||
K.E('span', {}, 'Dongle: ' + (zigbee.coordinator || '/dev/ttyUSB0')),
|
||||
K.E('span', {}, [
|
||||
'Status: ',
|
||||
zigbee.dongle_present
|
||||
? K.E('span', { 'style': 'color: var(--kiss-green, #22c55e);' }, '● Present')
|
||||
: K.E('span', { 'style': 'color: var(--kiss-red, #ef4444);' }, '● Not Found')
|
||||
]),
|
||||
K.E('span', {}, ['Paired devices: ', K.E('strong', { 'style': 'color: var(--kiss-green);' }, String(zigbeeDevices.length))])
|
||||
createCard('Zigbee Coordinator', '📻', zigbee.enabled, [
|
||||
E('div', { 'style': 'display:flex;flex-wrap:wrap;gap:16px;margin-bottom:12px;align-items:center;' }, [
|
||||
E('span', { 'style': 'color:#e0e0e0;' }, 'Adapter: ' + (zigbee.adapter || 'zigbee2mqtt')),
|
||||
E('span', { 'style': 'color:#808090;' }, 'Dongle: ' + (zigbee.coordinator || '/dev/ttyUSB0')),
|
||||
statusDot(zigbee.dongle_present, zigbee.dongle_present ? 'Present' : 'Not Found'),
|
||||
createMetric('Paired devices', zigbeeDevices.length, '#00d4aa')
|
||||
]),
|
||||
self.deviceMiniTable(K, zigbeeDevices, ['hostname', 'vendor', 'device_type'])
|
||||
])
|
||||
deviceTable(zigbeeDevices, ['hostname', 'vendor', 'device_type'])
|
||||
], zigbee.enabled ? '#ffa500' : '#808090')
|
||||
]);
|
||||
|
||||
return KissTheme.wrap(content, 'admin/secubox/services/device-intel/emulators');
|
||||
},
|
||||
|
||||
emuCard: function(K, title, enabled, contentItems) {
|
||||
return K.E('div', { 'class': 'kiss-card', 'style': 'margin-bottom: 16px;' }, [
|
||||
K.E('div', { 'style': 'display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;' }, [
|
||||
K.E('div', { 'class': 'kiss-card-title', 'style': 'margin: 0;' }, title),
|
||||
enabled
|
||||
? K.E('span', { 'style': 'background: var(--kiss-green, #22c55e); color: #000; padding: 4px 12px; border-radius: 6px; font-size: 12px; font-weight: bold;' }, 'Enabled')
|
||||
: K.E('span', { 'style': 'background: var(--kiss-muted, #64748b); color: #fff; padding: 4px 12px; border-radius: 6px; font-size: 12px;' }, 'Disabled')
|
||||
])
|
||||
].concat(contentItems));
|
||||
},
|
||||
|
||||
deviceMiniTable: function(K, devices, fields) {
|
||||
if (devices.length === 0) {
|
||||
return K.E('p', { 'style': 'color: var(--kiss-muted); font-style: italic; text-align: center; padding: 20px;' },
|
||||
'No devices discovered from this emulator.');
|
||||
}
|
||||
|
||||
var headers = {
|
||||
'hostname': 'Name',
|
||||
'vendor': 'Model/Vendor',
|
||||
'device_type': 'Type',
|
||||
'mac': 'ID',
|
||||
'ip': 'IP'
|
||||
};
|
||||
|
||||
return K.E('table', { 'class': 'kiss-table' }, [
|
||||
K.E('thead', {}, K.E('tr', {},
|
||||
fields.map(function(f) {
|
||||
return K.E('th', {}, headers[f] || f);
|
||||
})
|
||||
)),
|
||||
K.E('tbody', {},
|
||||
devices.slice(0, 20).map(function(d) {
|
||||
return K.E('tr', {},
|
||||
fields.map(function(f) {
|
||||
return K.E('td', {}, String(d[f] || '-'));
|
||||
})
|
||||
);
|
||||
})
|
||||
)
|
||||
]);
|
||||
return view;
|
||||
},
|
||||
|
||||
handleSaveApply: null,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user