From f65bc8c4caee8425877ae37ef2e8f555c61fb501 Mon Sep 17 00:00:00 2001 From: CyberMind-FR Date: Tue, 6 Jan 2026 19:17:56 +0100 Subject: [PATCH] fix: Array nesting issue in devices and applications table rendering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../view/secubox-netifyd/applications.js | 92 +++++++++--------- .../resources/view/secubox-netifyd/devices.js | 94 ++++++++++--------- 2 files changed, 95 insertions(+), 91 deletions(-) diff --git a/package/secubox/luci-app-secubox-netifyd/htdocs/luci-static/resources/view/secubox-netifyd/applications.js b/package/secubox/luci-app-secubox-netifyd/htdocs/luci-static/resources/view/secubox-netifyd/applications.js index fe0dac88..ffa3a545 100644 --- a/package/secubox/luci-app-secubox-netifyd/htdocs/luci-static/resources/view/secubox-netifyd/applications.js +++ b/package/secubox/luci-app-secubox-netifyd/htdocs/luci-static/resources/view/secubox-netifyd/applications.js @@ -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' }, [ diff --git a/package/secubox/luci-app-secubox-netifyd/htdocs/luci-static/resources/view/secubox-netifyd/devices.js b/package/secubox/luci-app-secubox-netifyd/htdocs/luci-static/resources/view/secubox-netifyd/devices.js index 76a2efa7..e59ffd30 100644 --- a/package/secubox/luci-app-secubox-netifyd/htdocs/luci-static/resources/view/secubox-netifyd/devices.js +++ b/package/secubox/luci-app-secubox-netifyd/htdocs/luci-static/resources/view/secubox-netifyd/devices.js @@ -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' }, [