From 6724d250d4b8c8f46c2a6f14862803ade66a2668 Mon Sep 17 00:00:00 2001 From: "Jokob @NetAlertX" <96159884+jokob-sk@users.noreply.github.com> Date: Mon, 2 Mar 2026 09:35:42 +0000 Subject: [PATCH] Refactor network tree data structure and improve device status handling - Updated the network tree data structure to use consistent naming conventions for device properties (e.g., devName, devMac). - Enhanced the initTree function to utilize the new property names and improved the rendering of device nodes. - Refactored the getStatusBadgeParts function to include additional parameters for archived and new device statuses. - Introduced convenience functions (badgeFromDevice and badgeFromDataAttrs) to streamline badge generation from device objects and data attributes. - Updated various language files to include new status labels and ensure consistency across translations. - Modified the renderSmallBox function to allow for custom icon HTML, improving flexibility in UI components. --- front/deviceDetails.php | 11 ++- front/devices.php | 41 ++++++--- front/js/device-columns.js | 9 ++ front/js/network-api.js | 36 ++++---- front/js/network-tabs.js | 43 ++++----- front/js/network-tree.js | 93 ++++++++++---------- front/js/ui_components.js | 111 +++++++++++++++--------- front/php/components/device_cards.php | 9 +- front/php/templates/language/ar_ar.json | 4 + front/php/templates/language/ca_ca.json | 4 + front/php/templates/language/cs_cz.json | 4 + front/php/templates/language/de_de.json | 4 + front/php/templates/language/en_us.json | 7 +- front/php/templates/language/es_es.json | 4 + front/php/templates/language/fa_fa.json | 4 + front/php/templates/language/fr_fr.json | 4 + front/php/templates/language/it_it.json | 4 + front/php/templates/language/ja_jp.json | 4 + front/php/templates/language/nb_no.json | 4 + front/php/templates/language/pl_pl.json | 4 + front/php/templates/language/pt_br.json | 4 + front/php/templates/language/pt_pt.json | 4 + front/php/templates/language/ru_ru.json | 4 + front/php/templates/language/sv_sv.json | 4 + front/php/templates/language/tr_tr.json | 4 + front/php/templates/language/uk_ua.json | 4 + front/php/templates/language/vi_vn.json | 4 + front/php/templates/language/zh_cn.json | 4 + 28 files changed, 280 insertions(+), 156 deletions(-) diff --git a/front/deviceDetails.php b/front/deviceDetails.php index 792c32a8..ec597bb9 100755 --- a/front/deviceDetails.php +++ b/front/deviceDetails.php @@ -425,17 +425,22 @@ async function renderSmallBoxes() { const deviceData = await response.json(); + // Derive status card appearance from shared getStatusBadgeParts — + // ensures icon, color, label and lang key are always in sync with the rest of the UI. + const statusBadge = badgeFromDevice(deviceData); + const statusText = statusBadge.label; + // Prepare custom data const customData = [ { "onclickEvent": "$('#tabDetails').trigger('click')", - "color": "bg-aqua", + "color": statusBadge.cssClass, "headerId": "deviceStatus", "headerStyle": "margin-left: 0em", "labelLang": "DevDetail_Shortcut_CurrentStatus", "iconId": "deviceStatusIcon", - "iconClass": deviceData.devPresentLastScan == 1 ? "fa fa-check text-green" : "fa fa-xmark text-red", - "dataValue": deviceData.devPresentLastScan == 1 ? getString("Gen_Online") : getString("Gen_Offline") + "iconHtml": statusBadge.iconHtml, + "dataValue": statusText }, { "onclickEvent": "$('#tabSessions').trigger('click');", diff --git a/front/devices.php b/front/devices.php index 3de3080f..a45ba884 100755 --- a/front/devices.php +++ b/front/devices.php @@ -520,6 +520,22 @@ function mapColumnIndexToFieldName(index, tableColumnVisible) { } +// --------------------------------------------------------- +// Status badge helper for DataTables rowData (positional array). +// Uses mapIndx(COL.*) for reordered display fields and COL_EXTRA.* for extra fields. +function badgeFromRowData(rowData) { + return getStatusBadgeParts( + rowData[mapIndx(COL.devPresentLastScan)], + rowData[mapIndx(COL.devAlertDown)], + rowData[mapIndx(COL.devFlapping)], + rowData[mapIndx(COL.devMac)], + '', + rowData[COL_EXTRA.devIsSleeping] || 0, + rowData[COL_EXTRA.devIsArchived] || 0, + rowData[COL_EXTRA.devIsNew] || 0 + ); +} + // --------------------------------------------------------- // Initializes the main devices list datatable function initializeDatatable (status) { @@ -657,6 +673,11 @@ function initializeDatatable (status) { for (let index = 0; index < tableColumnOrder.length; index++) { newRow.push(originalRow[tableColumnOrder[index]]); } + // Append extra (non-display) fields after the display columns so + // they are accessible in createdCell via COL_EXTRA.* + GRAPHQL_EXTRA_FIELDS.forEach(field => { + newRow.push(device[field] ?? (NUMERIC_DEFAULTS.has(field) ? 0 : "")); + }); return newRow; }); } @@ -717,8 +738,11 @@ function initializeDatatable (status) { data-relationship="${rowData[mapIndx(COL.devParentRelType)]}" data-status="${rowData[mapIndx(COL.devStatus)]}" data-present="${rowData[mapIndx(COL.devPresentLastScan)]}" - data-alert="${rowData[mapIndx(COL.devAlertDown)]}" + data-alertdown="${rowData[mapIndx(COL.devAlertDown)]}" data-flapping="${rowData[mapIndx(COL.devFlapping)]}" + data-sleeping="${rowData[COL_EXTRA.devIsSleeping] || 0}" + data-archived="${rowData[COL_EXTRA.devIsArchived] || 0}" + data-isnew="${rowData[COL_EXTRA.devIsNew] || 0}" data-icon="${rowData[mapIndx(COL.devIcon)]}"> ${displayedValue} @@ -860,20 +884,9 @@ function initializeDatatable (status) { {targets: [mapIndx(COL.devStatus)], 'createdCell': function (td, cellData, rowData, row, col) { - tmp_devPresentLastScan = rowData[mapIndx(COL.devPresentLastScan)] - tmp_devAlertDown = rowData[mapIndx(COL.devAlertDown)] - tmp_devMac = rowData[mapIndx(COL.devMac)] - tmp_devFlapping = rowData[mapIndx(COL.devFlapping)] + const badge = badgeFromRowData(rowData); - const badge = getStatusBadgeParts( - tmp_devPresentLastScan, // tmp_devPresentLastScan - tmp_devAlertDown, // tmp_devAlertDown - tmp_devFlapping, // tmp_devFlapping - tmp_devMac, // MAC - cellData // optional text - ); - - $(td).html (`${badge.iconHtml} ${badge.text}`); + $(td).html(`${badge.iconHtml} ${badge.label}`); } }, ], diff --git a/front/js/device-columns.js b/front/js/device-columns.js index 5d68c9ec..ea0420c5 100644 --- a/front/js/device-columns.js +++ b/front/js/device-columns.js @@ -75,8 +75,17 @@ const GRAPHQL_EXTRA_FIELDS = [ "devLastNotification", "devIsNew", "devIsArchived", + "devIsSleeping", ]; +// Row positions for extra (non-display) fields. +// In dataSrc, extra fields are appended AFTER the display columns in each row, +// so their position = DEVICE_COLUMN_FIELDS.length + their index in GRAPHQL_EXTRA_FIELDS. +// Use COL_EXTRA.fieldName to access them in createdCell rowData. +const COL_EXTRA = Object.fromEntries( + GRAPHQL_EXTRA_FIELDS.map((name, i) => [name, DEVICE_COLUMN_FIELDS.length + i]) +); + // Maps Device_TableHead_* language keys to their GraphQL/DB field names. // Used by getColumnNameFromLangString() in ui_components.js and by // column filter logic in devices.php. diff --git a/front/js/network-api.js b/front/js/network-api.js index 01addaef..e1c28ffd 100644 --- a/front/js/network-api.js +++ b/front/js/network-api.js @@ -16,15 +16,16 @@ function loadNetworkNodes() { // PC (leaf) <------- leafs are not included in this SQL query const rawSql = ` SELECT - parent.devName AS node_name, - LOWER(parent.devMac) AS node_mac, - parent.devPresentLastScan AS online, - parent.devType AS node_type, - LOWER(parent.devParentMAC) AS parent_mac, - parent.devIcon AS node_icon, - parent.devAlertDown AS node_alert, - parent.devFlapping AS node_flapping, - parent.devIsSleeping AS node_sleeping, + parent.devName, + LOWER(parent.devMac) AS devMac, + parent.devPresentLastScan, + parent.devType, + LOWER(parent.devParentMAC) AS devParentMAC, + parent.devIcon, + parent.devAlertDown, + parent.devFlapping, + parent.devIsSleeping, + parent.devIsNew, COUNT(child.devMac) AS node_ports_count FROM DevicesView AS parent LEFT JOIN DevicesView AS child @@ -35,7 +36,7 @@ function loadNetworkNodes() { WHERE parent.devType IN (${networkDeviceTypes}) AND parent.devIsArchived = 0 GROUP BY parent.devMac, parent.devName, parent.devPresentLastScan, - parent.devType, parent.devParentMAC, parent.devIcon, parent.devAlertDown, parent.devFlapping, parent.devIsSleeping + parent.devType, parent.devParentMAC, parent.devIcon, parent.devAlertDown, parent.devFlapping, parent.devIsSleeping, parent.devIsNew ORDER BY parent.devName; `; @@ -142,15 +143,8 @@ function loadDeviceTable({ sql, containerSelector, tableId, wrapperHtml = null, data: 'devStatus', width: '15%', render: function (_, type, device) { - const badge = getStatusBadgeParts( - device.devPresentLastScan, - device.devAlertDown, - device.devFlapping, - device.devMac, - device.devStatus, - device.devIsSleeping || 0 - ); - return `${badge.iconHtml} ${badge.text}`; + const badge = badgeFromDevice(device); + return `${badge.iconHtml} ${badge.label}`; } }, { @@ -206,7 +200,7 @@ function loadDeviceTable({ sql, containerSelector, tableId, wrapperHtml = null, */ function loadUnassignedDevices() { const sql = ` - SELECT devMac, devPresentLastScan, devName, devLastIP, devVendor, devAlertDown, devParentPort, devFlapping, devIsSleeping, devStatus + SELECT devMac, devPresentLastScan, devName, devLastIP, devVendor, devAlertDown, devParentPort, devFlapping, devIsSleeping, devIsNew, devStatus FROM DevicesView WHERE (devParentMAC IS NULL OR devParentMAC IN ("", " ", "undefined", "null")) AND LOWER(devMac) NOT LIKE "%internet%" @@ -241,7 +235,7 @@ function loadConnectedDevices(node_mac) { const normalized_mac = node_mac.toLowerCase(); const sql = ` - SELECT devName, devMac, devLastIP, devVendor, devPresentLastScan, devAlertDown, devParentPort, devVlan, devFlapping, devIsSleeping, + SELECT devName, devMac, devLastIP, devVendor, devPresentLastScan, devAlertDown, devParentPort, devVlan, devFlapping, devIsSleeping, devIsNew, devIsArchived, CASE WHEN devIsNew = 1 THEN 'New' WHEN devPresentLastScan = 1 THEN 'On-line' diff --git a/front/js/network-tabs.js b/front/js/network-tabs.js index d995da31..855ba9e0 100644 --- a/front/js/network-tabs.js +++ b/front/js/network-tabs.js @@ -8,19 +8,19 @@ function renderNetworkTabs(nodes) { let html = ''; nodes.forEach((node, i) => { - const iconClass = node.online == 1 ? "text-green" : - (node.node_sleeping == 1 ? "text-aqua" : - (node.node_alert == 1 ? "text-red" : "text-gray50")); + const iconClass = node.devPresentLastScan == 1 ? "text-green" : + (node.devIsSleeping == 1 ? "text-aqua" : + (node.devAlertDown == 1 ? "text-red" : "text-gray50")); const portLabel = node.node_ports_count ? ` (${node.node_ports_count})` : ''; - const icon = atob(node.node_icon); - const id = node.node_mac.replace(/:/g, '_'); + const icon = atob(node.devIcon); + const id = node.devMac.replace(/:/g, '_'); html += `