fix: Array nesting issue in devices and applications table rendering
Fixed "[object HTMLDivElement]" display bug in device and application list views.
## Problem:
- Device list showed "[object HTMLDivElement],[object HTMLDivElement],..." instead of table rows
- Applications list had the same issue
- Root cause: `sortedDevices.map()` and `sortedApps.map()` return arrays, but these arrays were being nested incorrectly in the E() children array
## Solution:
Changed table row structure from:
```javascript
E('div', { 'class': 'table' }, [
E('div', { 'class': 'tr table-titles' }, [...]), // header
sortedDevices.map(function(device) { // array nested wrong!
return E('div', {...});
})
])
```
To:
```javascript
E('div', { 'class': 'table' },
[
E('div', { 'class': 'tr table-titles' }, [...]) // header
].concat(
sortedDevices.map(function(device) { // properly flattened!
return E('div', {...});
})
)
)
```
## Technical Details:
- The E() helper expects children to be individual DOM elements, not nested arrays
- Using `.concat()` properly flattens the array of row elements
- Applied fix to both devices.js and applications.js views
## Testing:
- Deployed to router
- Device list now displays all 6 detected devices with IP, MAC, traffic stats
- Applications list displays all 4 application categories correctly
- Table formatting and styling render properly
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
faf9ee3265
commit
f65bc8c4ca
@ -169,50 +169,51 @@ return view.extend({
|
||||
}.bind(this);
|
||||
|
||||
dom.content(container, [
|
||||
apps.length > 0 ? E('div', { 'class': 'table', 'style': 'font-size: 0.95em' }, [
|
||||
// Header
|
||||
E('div', { 'class': 'tr table-titles' }, [
|
||||
E('div', {
|
||||
'class': 'th left',
|
||||
'style': 'width: 30%; cursor: pointer',
|
||||
'click': ui.createHandlerFn(this, 'handleSort', 'name')
|
||||
}, [
|
||||
_('Application'),
|
||||
' ',
|
||||
getSortIcon('name')
|
||||
]),
|
||||
E('div', {
|
||||
'class': 'th center',
|
||||
'style': 'width: 15%; cursor: pointer',
|
||||
'click': ui.createHandlerFn(this, 'handleSort', 'flows')
|
||||
}, [
|
||||
_('Flows'),
|
||||
' ',
|
||||
getSortIcon('flows')
|
||||
]),
|
||||
E('div', {
|
||||
'class': 'th center',
|
||||
'style': 'width: 15%; cursor: pointer',
|
||||
'click': ui.createHandlerFn(this, 'handleSort', 'packets')
|
||||
}, [
|
||||
_('Packets'),
|
||||
' ',
|
||||
getSortIcon('packets')
|
||||
]),
|
||||
E('div', {
|
||||
'class': 'th right',
|
||||
'style': 'width: 20%; cursor: pointer',
|
||||
'click': ui.createHandlerFn(this, 'handleSort', 'bytes')
|
||||
}, [
|
||||
_('Total Traffic'),
|
||||
' ',
|
||||
getSortIcon('bytes')
|
||||
]),
|
||||
E('div', { 'class': 'th', 'style': 'width: 20%' }, _('% of Total'))
|
||||
]),
|
||||
|
||||
// Rows
|
||||
sortedApps.map(function(app, idx) {
|
||||
apps.length > 0 ? E('div', { 'class': 'table', 'style': 'font-size: 0.95em' },
|
||||
[
|
||||
// Header
|
||||
E('div', { 'class': 'tr table-titles' }, [
|
||||
E('div', {
|
||||
'class': 'th left',
|
||||
'style': 'width: 30%; cursor: pointer',
|
||||
'click': ui.createHandlerFn(this, 'handleSort', 'name')
|
||||
}, [
|
||||
_('Application'),
|
||||
' ',
|
||||
getSortIcon('name')
|
||||
]),
|
||||
E('div', {
|
||||
'class': 'th center',
|
||||
'style': 'width: 15%; cursor: pointer',
|
||||
'click': ui.createHandlerFn(this, 'handleSort', 'flows')
|
||||
}, [
|
||||
_('Flows'),
|
||||
' ',
|
||||
getSortIcon('flows')
|
||||
]),
|
||||
E('div', {
|
||||
'class': 'th center',
|
||||
'style': 'width: 15%; cursor: pointer',
|
||||
'click': ui.createHandlerFn(this, 'handleSort', 'packets')
|
||||
}, [
|
||||
_('Packets'),
|
||||
' ',
|
||||
getSortIcon('packets')
|
||||
]),
|
||||
E('div', {
|
||||
'class': 'th right',
|
||||
'style': 'width: 20%; cursor: pointer',
|
||||
'click': ui.createHandlerFn(this, 'handleSort', 'bytes')
|
||||
}, [
|
||||
_('Total Traffic'),
|
||||
' ',
|
||||
getSortIcon('bytes')
|
||||
]),
|
||||
E('div', { 'class': 'th', 'style': 'width: 20%' }, _('% of Total'))
|
||||
])
|
||||
].concat(
|
||||
// Rows
|
||||
sortedApps.map(function(app, idx) {
|
||||
var percentage = totalBytes > 0 ? (app.bytes / totalBytes * 100) : 0;
|
||||
var color = appColors[idx % appColors.length];
|
||||
|
||||
@ -250,7 +251,8 @@ return view.extend({
|
||||
])
|
||||
]);
|
||||
}.bind(this))
|
||||
]) : E('div', {
|
||||
)
|
||||
) : E('div', {
|
||||
'class': 'alert-message info',
|
||||
'style': 'text-align: center; padding: 3rem'
|
||||
}, [
|
||||
|
||||
@ -172,51 +172,52 @@ return view.extend({
|
||||
}.bind(this);
|
||||
|
||||
dom.content(container, [
|
||||
devices.length > 0 ? E('div', { 'class': 'table', 'style': 'font-size: 0.95em' }, [
|
||||
// Header
|
||||
E('div', { 'class': 'tr table-titles' }, [
|
||||
E('div', {
|
||||
'class': 'th left',
|
||||
'style': 'width: 20%; cursor: pointer',
|
||||
'click': ui.createHandlerFn(this, 'handleSort', 'ip')
|
||||
}, [
|
||||
_('IP Address'),
|
||||
' ',
|
||||
getSortIcon('ip')
|
||||
]),
|
||||
E('div', { 'class': 'th left', 'style': 'width: 20%' }, _('MAC Address')),
|
||||
E('div', {
|
||||
'class': 'th center',
|
||||
'style': 'width: 10%; cursor: pointer',
|
||||
'click': ui.createHandlerFn(this, 'handleSort', 'flows')
|
||||
}, [
|
||||
_('Flows'),
|
||||
' ',
|
||||
getSortIcon('flows')
|
||||
]),
|
||||
E('div', {
|
||||
'class': 'th right',
|
||||
'style': 'width: 15%; cursor: pointer',
|
||||
'click': ui.createHandlerFn(this, 'handleSort', 'bytes_sent')
|
||||
}, [
|
||||
_('Sent'),
|
||||
' ',
|
||||
getSortIcon('bytes_sent')
|
||||
]),
|
||||
E('div', {
|
||||
'class': 'th right',
|
||||
'style': 'width: 15%; cursor: pointer',
|
||||
'click': ui.createHandlerFn(this, 'handleSort', 'bytes_received')
|
||||
}, [
|
||||
_('Received'),
|
||||
' ',
|
||||
getSortIcon('bytes_received')
|
||||
]),
|
||||
E('div', { 'class': 'th', 'style': 'width: 20%' }, _('Traffic Distribution'))
|
||||
]),
|
||||
|
||||
// Rows
|
||||
sortedDevices.map(function(device, idx) {
|
||||
devices.length > 0 ? E('div', { 'class': 'table', 'style': 'font-size: 0.95em' },
|
||||
[
|
||||
// Header
|
||||
E('div', { 'class': 'tr table-titles' }, [
|
||||
E('div', {
|
||||
'class': 'th left',
|
||||
'style': 'width: 20%; cursor: pointer',
|
||||
'click': ui.createHandlerFn(this, 'handleSort', 'ip')
|
||||
}, [
|
||||
_('IP Address'),
|
||||
' ',
|
||||
getSortIcon('ip')
|
||||
]),
|
||||
E('div', { 'class': 'th left', 'style': 'width: 20%' }, _('MAC Address')),
|
||||
E('div', {
|
||||
'class': 'th center',
|
||||
'style': 'width: 10%; cursor: pointer',
|
||||
'click': ui.createHandlerFn(this, 'handleSort', 'flows')
|
||||
}, [
|
||||
_('Flows'),
|
||||
' ',
|
||||
getSortIcon('flows')
|
||||
]),
|
||||
E('div', {
|
||||
'class': 'th right',
|
||||
'style': 'width: 15%; cursor: pointer',
|
||||
'click': ui.createHandlerFn(this, 'handleSort', 'bytes_sent')
|
||||
}, [
|
||||
_('Sent'),
|
||||
' ',
|
||||
getSortIcon('bytes_sent')
|
||||
]),
|
||||
E('div', {
|
||||
'class': 'th right',
|
||||
'style': 'width: 15%; cursor: pointer',
|
||||
'click': ui.createHandlerFn(this, 'handleSort', 'bytes_received')
|
||||
}, [
|
||||
_('Received'),
|
||||
' ',
|
||||
getSortIcon('bytes_received')
|
||||
]),
|
||||
E('div', { 'class': 'th', 'style': 'width: 20%' }, _('Traffic Distribution'))
|
||||
])
|
||||
].concat(
|
||||
// Rows
|
||||
sortedDevices.map(function(device, idx) {
|
||||
var lastSeen = device.last_seen || 0;
|
||||
var now = Math.floor(Date.now() / 1000);
|
||||
var ago = now - lastSeen;
|
||||
@ -278,7 +279,8 @@ return view.extend({
|
||||
])
|
||||
]);
|
||||
}.bind(this))
|
||||
]) : E('div', {
|
||||
)
|
||||
) : E('div', {
|
||||
'class': 'alert-message info',
|
||||
'style': 'text-align: center; padding: 3rem'
|
||||
}, [
|
||||
|
||||
Loading…
Reference in New Issue
Block a user