refactor, tab async loading on focus

This commit is contained in:
jokob-sk
2025-07-26 12:58:45 +10:00
parent cd7cbcc4c8
commit 54fa2743f9
14 changed files with 535 additions and 364 deletions

View File

@@ -1,3 +1,8 @@
<span class="helpIcon">
<a target="_blank" href="https://github.com/jokob-sk/NetAlertX/blob/main/docs/WORKFLOWS_DEBUGGING.md">
<i class="fa fa-circle-question"></i>
</a>
</span>
<section class="content"> <section class="content">
<div class="nav-tabs-custom app-event-content" style="margin-bottom: 0px;"> <div class="nav-tabs-custom app-event-content" style="margin-bottom: 0px;">
<ul id="tabs-location" class="nav nav-tabs col-sm-2 hidden"> <ul id="tabs-location" class="nav nav-tabs col-sm-2 hidden">

View File

@@ -2090,36 +2090,45 @@ input[readonly] {
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
Spin Spin
----------------------------------------------------------------------------- */ ----------------------------------------------------------------------------- */
#loadingSpinner {
position: fixed;
z-index: 1000;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
transition: opacity 0.3s ease-in-out;
pointer-events: none;
display: block;
z-index: 999;
}
#loadingSpinner.visible {
opacity: 1;
pointer-events: auto;
}
.pa_semitransparent-panel { .pa_semitransparent-panel {
position: absolute; position: absolute;
width: 100%; /*calc (100% -40px);*/ width: 100%;
height: 100%; height: 100%;
left: 0;
top: 0;
display: block;
opacity: 0.8;
background-color: #fff; background-color: #fff;
z-index: 800; opacity: 0.8;
z-index: 99;
} }
.pa_spinner { .pa_spinner {
position: fixed; position: absolute;
left: 0;
right: 0;
top: 100px; top: 100px;
margin-left: auto; left: 50%;
margin-right: auto; transform: translateX(-50%);
padding: 15px; padding: 15px;
width: 200px; width: 200px;
background-color: #fff; background-color: #fff;
z-index: 801; z-index: 1000;
} }
#loadingSpinner
{
z-index: 100;
}
/* Multi-edit adjustements */ /* Multi-edit adjustements */
.box-header .box-header

View File

@@ -10,6 +10,16 @@
* *
* Additional fixes For Pi.Alert UI by leiweibau */ * Additional fixes For Pi.Alert UI by leiweibau */
:root {
--color-aqua: #00c0ef;
--color-lightblue: #3c8dbc;
--color-blue: #0060df;
--color-green: #00a65a;
--color-yellow: #f39c12;
--color-red: #dd4b39;
--color-gray: #8c8c8c;
}
:root { :root {
--datatable-bgcolor: rgba(64, 76, 88, 0.8); --datatable-bgcolor: rgba(64, 76, 88, 0.8);
} }
@@ -770,4 +780,9 @@ input[type="password"]::-webkit-caps-lock-indicator {
.thresholdFormControl .thresholdFormControl
{ {
color:#000; color:#000;
}
.btn:hover
{
color: var(--color-gray);
} }

View File

@@ -11,6 +11,16 @@
* Additional fixes For Pi.Alert UI by leiweibau */ * Additional fixes For Pi.Alert UI by leiweibau */
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
:root {
--color-aqua: #00c0ef;
--color-lightblue: #3c8dbc;
--color-blue: #0060df;
--color-green: #00a65a;
--color-yellow: #f39c12;
--color-red: #dd4b39;
--color-gray: #8c8c8c;
}
:root { :root {
--datatable-bgcolor: rgba(64, 76, 88, 0.8); --datatable-bgcolor: rgba(64, 76, 88, 0.8);
@@ -781,3 +791,7 @@
color:#000; color:#000;
} }
.btn:hover
{
color: var(--color-gray);
}

View File

@@ -16,8 +16,9 @@
require 'php/templates/header.php'; require 'php/templates/header.php';
?> ?>
<script>
showSpinner();
</script>
<!-- Page ------------------------------------------------------------------ --> <!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper"> <div class="content-wrapper">
@@ -125,15 +126,11 @@
<div class="tab-content" style="min-height: 430px;"> <div class="tab-content" style="min-height: 430px;">
<!-- tab page 1 ------------------------------------------------------------ --> <!-- tab page 1 ------------------------------------------------------------ -->
<!--
<div class="tab-pane fade in active" id="panDetails">
-->
<div class="tab-pane fade" id="panDetails">
<div class="tab-pane fade" id="panDetails">
<?php <?php
require 'deviceDetailsEdit.php'; require 'deviceDetailsEdit.php';
?> ?>
</div> </div>
<!-- tab page 2 ------------------------------------------------------------ --> <!-- tab page 2 ------------------------------------------------------------ -->
@@ -141,51 +138,38 @@
<?php <?php
require 'deviceDetailsSessions.php'; require 'deviceDetailsSessions.php';
?> ?>
</div> </div>
<!-- tab page "Tools" ------------------------------------------------------------ --> <!-- tab page "Tools" ------------------------------------------------------------ -->
<div class="tab-pane fade" id="panTools"> <div class="tab-pane fade" id="panTools">
<?php <?php
require 'deviceDetailsTools.php'; require 'deviceDetailsTools.php';
?> ?>
</div> </div>
<!-- tab page 3 ------------------------------------------------------------ --> <!-- tab page 3 ------------------------------------------------------------ -->
<div class="tab-pane fade table-responsive" id="panPresence"> <div class="tab-pane fade table-responsive" id="panPresence">
<?php <?php
// Include the other page // Include the other page
include 'deviceDetailsPresence.php'; include 'deviceDetailsPresence.php';
?> ?>
</div> </div>
<!-- tab page 4 ------------------------------------------------------------ --> <!-- tab page 4 ------------------------------------------------------------ -->
<div class="tab-pane fade table-responsive" id="panEvents"> <div class="tab-pane fade table-responsive" id="panEvents">
<?php <?php
// Include the other page // Include the other page
include 'deviceDetailsEvents.php'; include 'deviceDetailsEvents.php';
?> ?>
</div> </div>
<!-- tab page 7 ------------------------------------------------------------ --> <!-- tab page 7 ------------------------------------------------------------ -->
<div class="tab-pane fade table-responsive" id="panPlugins"> <div class="tab-pane fade table-responsive" id="panPlugins">
<?php <?php
// Include the other page // Include the other page
include 'pluginsCore.php'; include 'pluginsCore.php';
?> ?>
</div> </div>
</div> </div>
@@ -273,7 +257,7 @@ function main () {
period = '1 day'; period = '1 day';
sessionsRows = 50; sessionsRows = 50;
eventsRows = 50; eventsRows = 50;
$('#chkHideConnectionEvents')[0].checked = eval(eventsHide == 'true'); // $('#chkHideConnectionEvents')[0].checked = eval(eventsHide == 'true');
// Initialize components with parameters // Initialize components with parameters
@@ -282,26 +266,7 @@ function main () {
$( document ).ready(function() { $( document ).ready(function() {
initializeTabs(); initializeTabs();
}); });
// // Events tab toggle conenction events
// $('input').on('ifToggled', function(event){
// // Hide / Show Events
// if (event.currentTarget.id == 'chkHideConnectionEvents') {
// getDeviceEvents();
// } else {
// // Activate save & restore
// // activateSaveRestoreData();
// // Ask skip notifications
// // if (event.currentTarget.id == 'chkArchived' ) {
// // askSkipNotifications();
// // }
// }
// });
} }
@@ -328,6 +293,48 @@ function recordSwitch(direction) {
} }
} }
// ----------------------------------------
// Handle previous/next arrows/chevrons
function updateChevrons(currentMac) {
const devicesList = getDevicesList();
// Find the index of the device by MAC
pos = devicesList.findIndex(item => item.devMac == currentMac);
// If device not found, optionally add it or handle error
if (pos === -1) {
// If you want to add a placeholder or handle missing device:
// devicesList.push({ mac: currentMac, name: 'Unknown', type: 'Unknown' });
// pos = devicesList.length - 1;
// Or just return early if device not found
console.warn('Device with MAC not found:', currentMac);
return;
}
// Update the record number display
$('#txtRecord').html((pos + 1) + ' / ' + devicesList.length);
// Enable/disable previous button
if (pos <= 0) {
$('#btnPrevious').attr('disabled', '');
$('#btnPrevious').addClass('text-gray50');
} else {
$('#btnPrevious').removeAttr('disabled');
$('#btnPrevious').removeClass('text-gray50');
}
// Enable/disable next button
if (pos >= devicesList.length - 1) {
$('#btnNext').attr('disabled', '');
$('#btnNext').addClass('text-gray50');
} else {
$('#btnNext').removeAttr('disabled');
$('#btnNext').removeClass('text-gray50');
}
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
function performSwitch(direction) function performSwitch(direction)
@@ -338,7 +345,9 @@ function performSwitch(direction)
// Update the global position in the devices list variable 'pos' // Update the global position in the devices list variable 'pos'
if (direction === "next") { if (direction === "next") {
if (pos < devicesList.length - 1) { console.log("direction" + direction);
if (pos < devicesList.length) {
pos++; pos++;
} }
} else if (direction === "prev") { } else if (direction === "prev") {
@@ -358,15 +367,12 @@ function performSwitch(direction)
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Activate save & restore on any value change // Activate save & restore on any value change
$(document).on('input', 'input:text', function() { $(document).on('input', 'input:text', function() {
settingsChanged(); settingsChanged();
}); });
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
function initializeTabs () { function initializeTabs () {
@@ -380,8 +386,6 @@ function initializeTabs () {
} }
$('.nav-tabs a[id='+ selectedTab +']').tab('show'); $('.nav-tabs a[id='+ selectedTab +']').tab('show');
// $('.nav-tabs a[id='+ selectedTab +']').parent().click();
// $('.nav-tabs a[id="tabPlugins"]').tab('show');
// When changed save new current tab // When changed save new current tab
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) { $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
@@ -391,11 +395,6 @@ function initializeTabs () {
// events on tab change // events on tab change
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) { $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
var target = $(e.target).attr("href") // activated tab var target = $(e.target).attr("href") // activated tab
// if(target == "#panTools")
// {
// // loadTools();
// }
}); });
} }
@@ -482,22 +481,41 @@ async function renderSmallBoxes() {
console.error('Error in renderSmallBoxes:', error); console.error('Error in renderSmallBoxes:', error);
} finally { } finally {
// Hide loading dialog // Hide loading dialog
hideSpinner(); // hideSpinner();
} }
} }
function updateDevicePageName(mac) {
name = getDevDataByMac(mac, "devName")
owner = getDevDataByMac(mac, "devOwner")
// Page title - Name
if (mac == "new") {
$('#pageTitle').html(`<i title="${getString("Gen_create_new_device")}" class="fa fa-square-plus"></i> ` + getString("Gen_create_new_device"));
$('#devicePageInfoPlc .inner').html(`<i class="fa fa-circle-info"></i> ` + getString("Gen_create_new_device_info"));
$('#devicePageInfoPlc').show();
} else if (owner == null || owner == '' ||
(name.toString()).indexOf(owner) != -1) {
$('#pageTitle').html(name);
$('#devicePageInfoPlc').hide();
} else {
$('#pageTitle').html(name + ' (' + owner + ')');
$('#devicePageInfoPlc').hide();
}
}
//----------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------
window.onload = function async() window.onload = function async()
{ {
initializeTabs(); initializeTabs();
updateChevrons(mac);
updateDevicePageName(mac);
} }
</script> </script>

View File

@@ -39,7 +39,7 @@
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Get plugin and settings data from API endpoints // Get plugin and settings data from API endpoints
function getDeviceData(readAllData){ function getDeviceData(){
mac = getMac() mac = getMac()
@@ -53,13 +53,13 @@
var deviceData = JSON.parse(data); var deviceData = JSON.parse(data);
// Deactivate next previous buttons // // Deactivate next previous buttons
if (readAllData) { // if (readAllData) {
$('#btnPrevious').attr ('disabled',''); // $('#btnPrevious').attr ('disabled','');
$('#btnPrevious').addClass ('text-gray50'); // $('#btnPrevious').addClass ('text-gray50');
$('#btnNext').attr ('disabled',''); // $('#btnNext').attr ('disabled','');
$('#btnNext').addClass ('text-gray50'); // $('#btnNext').addClass ('text-gray50');
} // }
// some race condition, need to implement delay // some race condition, need to implement delay
setTimeout(() => { setTimeout(() => {
@@ -255,30 +255,13 @@
updateAllIconPreviews(); updateAllIconPreviews();
// update readonly fields // update readonly fields
handleReadOnly(settingsData, disabledFields); handleReadOnly(settingsData, disabledFields);
// Page title - Name
if (mac == "new") {
$('#pageTitle').html(`<i title="${getString("Gen_create_new_device")}" class="fa fa-square-plus"></i> ` + getString("Gen_create_new_device"));
$('#devicePageInfoPlc .inner').html(`<i class="fa fa-circle-info"></i> ` + getString("Gen_create_new_device_info"));
$('#devicePageInfoPlc').show();
} else if (deviceData['devOwner'] == null || deviceData['devOwner'] == '' ||
(deviceData['devName'].toString()).indexOf(deviceData['devOwner']) != -1) {
$('#pageTitle').html(deviceData['devName']);
$('#devicePageInfoPlc').hide();
} else {
$('#pageTitle').html(deviceData['devName'] + ' (' + deviceData['devOwner'] + ')');
$('#devicePageInfoPlc').hide();
}
}; };
// console.log(relevantSettings) // console.log(relevantSettings)
generateSimpleForm(relevantSettings); generateSimpleForm(relevantSettings);
// <> chevrons
updateChevrons(deviceData)
toggleNetworkConfiguration(mac == 'Internet') toggleNetworkConfiguration(mac == 'Internet')
initSelect2(); initSelect2();
@@ -294,46 +277,6 @@
} }
// ----------------------------------------
// Handle previous/next arrows/chevrons
function updateChevrons(deviceData) {
devicesList = getDevicesList();
// console.log(devicesList);
// Check if device is part of the devicesList
pos = devicesList.findIndex(item => item.rowid == deviceData['rowid']);
// console.log(pos);
if (pos == -1) {
devicesList.push({"rowid" : deviceData['rowid'], "mac" : deviceData['devMac'], "name": deviceData['devName'], "type": deviceData['devType']});
pos=0;
}
// Record number
$('#txtRecord').html (pos+1 +' / '+ devicesList.length);
// Deactivate previous button
if (pos <= 0) {
$('#btnPrevious').attr ('disabled','');
$('#btnPrevious').addClass ('text-gray50');
} else {
$('#btnPrevious').removeAttr ('disabled');
$('#btnPrevious').removeClass ('text-gray50');
}
// Deactivate next button
if (pos >= (devicesList.length-1)) {
$('#btnNext').attr ('disabled','');
$('#btnNext').addClass ('text-gray50');
} else {
$('#btnNext').removeAttr ('disabled');
$('#btnNext').removeClass ('text-gray50');
}
}
// ---------------------------------------- // ----------------------------------------
// Handle the read-only fields // Handle the read-only fields
function handleReadOnly(settingsData, disabledFields) { function handleReadOnly(settingsData, disabledFields) {
@@ -451,9 +394,47 @@
} }
} }
// -----------------------------------------------
// INIT with polling for panel element visibility
// -----------------------------------------------
var deviceDetailsPageInitialized = false;
function initdeviceDetailsPage()
{
// Only proceed if .plugin-content is visible
if (!$('#panDetails:visible').length) {
return; // exit early if nothing is visible
}
// init page once
if (deviceDetailsPageInitialized) return; // ENSURE ONCE
deviceDetailsPageInitialized = true;
showSpinner();
getDeviceData();
}
// -----------------------------------------------------------------------------
// Recurring function to monitor the URL and reinitialize if needed
function deviceDetailsPageUpdater() {
initdeviceDetailsPage();
// Run updater again after delay
setTimeout(deviceDetailsPageUpdater, 200);
}
// if visible, load immediately, if not start updater
if (!$('#panDetails:visible').length) {
deviceDetailsPageUpdater();
}
else
{
getDeviceData();
}
// -------------------- INIT ------------------------
getDeviceData(true);
</script> </script>

View File

@@ -29,10 +29,7 @@
<script> <script>
var eventsRows = 10;
var eventsHide = true;
var parEventsRows = 'Front_Details_Events_Rows';
var parEventsHide = 'Front_Details_Events_Hide';
@@ -41,6 +38,8 @@ function loadEventsData() {
const hideConnections = $('#chkHideConnectionEvents')[0].checked; const hideConnections = $('#chkHideConnectionEvents')[0].checked;
const hideConnectionsStr = hideConnections ? 'true' : 'false'; const hideConnectionsStr = hideConnections ? 'true' : 'false';
mac = getMac()
const rawSql = ` const rawSql = `
SELECT eve_DateTime, eve_EventType, eve_IP, eve_AdditionalInfo SELECT eve_DateTime, eve_EventType, eve_IP, eve_AdditionalInfo
FROM Events FROM Events
@@ -56,8 +55,6 @@ function loadEventsData() {
// Manually load the data first // Manually load the data first
$.get(apiUrl, function (data) { $.get(apiUrl, function (data) {
const parsed = JSON.parse(data); const parsed = JSON.parse(data);
console.log(parsed);
const rows = parsed.map(row => { const rows = parsed.map(row => {
const rawDate = row.eve_DateTime; const rawDate = row.eve_DateTime;
@@ -70,62 +67,94 @@ function loadEventsData() {
]; ];
}); });
console.log(rows);
// Fill the table manually // Fill the table manually
const table = $('#tableEvents').DataTable(); const table = $('#tableEvents').DataTable();
table.clear(); table.clear();
table.rows.add(rows); // assuming each row is an array table.rows.add(rows); // assuming each row is an array
table.draw(); table.draw();
hideSpinner();
}); });
} }
function initializeSessionsDatatable () { function initializeEventsDatatable (eventsRows) {
if ($.fn.dataTable.isDataTable('#tableEvents')) {
$('#tableEvents').DataTable().clear().destroy();
}
// Events datatable
$('#tableEvents').DataTable({ $('#tableEvents').DataTable({
'paging' : true, 'paging' : true,
'lengthChange': true, 'lengthChange': true,
'lengthMenu' : [[10, 25, 50, 100, 500, -1], [10, 25, 50, 100, 500, 'All']], 'lengthMenu' : [[10, 25, 50, 100, 500, -1], [10, 25, 50, 100, 500, 'All']],
'searching' : true, 'searching' : true,
'ordering' : true, 'ordering' : true,
'info' : true, 'info' : true,
'autoWidth' : false, 'autoWidth' : false,
'order' : [[0,'desc']], 'order' : [[0,'desc']],
'pageLength' : eventsRows,
// Parameters 'columnDefs' : [
'pageLength' : eventsRows, {
targets: [0],
'columnDefs' : [ 'createdCell': function (td, cellData, rowData, row, col) {
// Replace HTML codes $(td).html(translateHTMLcodes(localizeTimestamp(cellData)));
{targets: [0], }
'createdCell': function (td, cellData, rowData, row, col) { }
$(td).html (translateHTMLcodes (localizeTimestamp(cellData))); ],
} }
],
// Processing
'processing' : true,
'language' : {
processing: '<table><td width="130px" align="middle"><?= lang("DevDetail_Loading");?></td>'+
'<td><i class="ion ion-ios-loop-strong fa-spin fa-2x fa-fw">'+
'</td></table>',
emptyTable: 'No data',
"lengthMenu": "<?= lang('Events_Tablelenght');?>",
"search": "<?= lang('Events_Searchbox');?>: ",
"paginate": {
"next": "<?= lang('Events_Table_nav_next');?>",
"previous": "<?= lang('Events_Table_nav_prev');?>"
},
"info": "<?= lang('Events_Table_info');?>",
}
});
'processing' : true,
'language' : {
processing: '<table><td width="130px" align="middle"><?= lang("DevDetail_Loading");?></td>'+
'<td><i class="ion ion-ios-loop-strong fa-spin fa-2x fa-fw"></i></td></table>',
emptyTable: 'No data',
"lengthMenu": "<?= lang('Events_Tablelenght');?>",
"search": "<?= lang('Events_Searchbox');?>: ",
"paginate": {
"next": "<?= lang('Events_Table_nav_next');?>",
"previous": "<?= lang('Events_Table_nav_prev');?>"
},
"info": "<?= lang('Events_Table_info');?>",
}
});
} }
initializeSessionsDatatable(); // -----------------------------------------------
loadEventsData(); // INIT with polling for panel element visibility
// -----------------------------------------------
var eventsPageInitialized = false;
function initDeviceEventsPage()
{
// Only proceed if .plugin-content is visible
if (!$('#panEvents:visible').length) {
return; // exit early if nothing is visible
}
// init page once
if (eventsPageInitialized) return; // ENSURE ONCE
eventsPageInitialized = true;
showSpinner();
var eventsRows = 10;
var eventsHide = true;
initializeEventsDatatable(eventsRows);
loadEventsData();
}
// -----------------------------------------------------------------------------
// Recurring function to monitor the URL and reinitialize if needed
function deviceEventsPageUpdater() {
initDeviceEventsPage();
// Run updater again after delay
setTimeout(deviceEventsPageUpdater, 200);
}
deviceEventsPageUpdater();
</script> </script>

View File

@@ -23,8 +23,6 @@
<script> <script>
initializeCalendar();
loadPresenceData();
// Force re-render calendar on tab change // Force re-render calendar on tab change
// (bugfix for render error at left panel) // (bugfix for render error at left panel)
@@ -234,6 +232,37 @@ function initializeCalendar() {
}) })
} }
// -----------------------------------------------
// INIT with polling for panel element visibility
// -----------------------------------------------
var presencePageInitialized = false;
function initDevicePresencePage() {
// Only proceed if the Presence tab is visible
if (!$('#panPresence:visible').length) {
return; // Exit early if nothing is visible
}
// Ensure initialization only happens once
if (presencePageInitialized) return;
presencePageInitialized = true;
showSpinner();
initializeCalendar();
loadPresenceData();
}
// Recurring check to initialize when visible
function devicePresencePageUpdater() {
initDevicePresencePage();
setTimeout(devicePresencePageUpdater, 200);
}
devicePresencePageUpdater();
</script> </script>

View File

@@ -24,77 +24,126 @@
<script> <script>
var parSessionsRows = 'Front_Details_Sessions_Rows';
function initializeSessionsDatatable (sessionsRows) {
// Sessions datatable
$('#tableSessions').DataTable({
'paging' : true,
'lengthChange': true,
'lengthMenu' : [[10, 25, 50, 100, 500, -1], [10, 25, 50, 100, 500, 'All']],
'searching' : true,
'ordering' : true,
'info' : true,
'autoWidth' : false,
'order' : [[0,'desc'], [1,'desc']],
// Parameters
'pageLength' : sessionsRows,
'columnDefs' : [
{visible: false, targets: [0]},
// Replace HTML codes
{targets: [3,5],
'createdCell': function (td, cellData, rowData, row, col) {
$(td).html (translateHTMLcodes (cellData));
} },
// Date
{targets: [1,2],
"createdCell": function (td, cellData, rowData, row, col) {
// console.log(cellData);
if (!cellData.includes("missing event") && !cellData.includes("..."))
{
if (cellData.includes("+")) { // Check if timezone offset is present
cellData = cellData.split('+')[0]; // Remove timezone offset
}
// console.log(cellData);
result = localizeTimestamp(cellData);
} else
{
result = translateHTMLcodes(cellData)
}
$(td).html (result);
} }
],
// Processing
'processing' : true,
'language' : {
processing: '<table><td width="130px" align="middle"><?= lang("DevDetail_Loading");?></td>'+
'<td><i class="ion ion-ios-loop-strong fa-spin fa-2x fa-fw">'+
'</td></table>',
emptyTable: 'No data',
"lengthMenu": "<?= lang('Events_Tablelenght');?>",
"search": "<?= lang('Events_Searchbox');?>: ",
"paginate": {
"next": "<?= lang('Events_Table_nav_next');?>",
"previous": "<?= lang('Events_Table_nav_prev');?>"
},
"info": "<?= lang('Events_Table_info');?>",
}
});
}
// -----------------------------------------------
// INIT with polling for panel element visibility
// -----------------------------------------------
// -----------------------------------------------------------
// Init datatable
function loadSessionsData(period){
const table = $('#tableSessions').DataTable();
showSpinner();
// table.clear().draw(); // Clear existing data before reloading
table.ajax
.url('php/server/events.php?action=getDeviceSessions&mac=' + getMac() + '&period=' + period)
.load(function () {
hideSpinner();
});
}
var sessionsPageInitialized = false;
// -----------------------------------------------------------
// Main init function
function initDeviceSessionsPage()
{
// Only proceed if .plugin-content is visible
if (!$('#panSessions:visible').length) {
return; // exit early if nothing is visible
}
// init page once
if (sessionsPageInitialized) return;
sessionsPageInitialized = true;
showSpinner();
var sessionsRows = 10; var sessionsRows = 10;
var period = '1 month'; var period = '1 month';
function initializeSessionsDatatable () { initializeSessionsDatatable(sessionsRows);
// Sessions datatable loadSessionsData(period);
$('#tableSessions').DataTable({ }
'paging' : true,
'lengthChange': true,
'lengthMenu' : [[10, 25, 50, 100, 500, -1], [10, 25, 50, 100, 500, 'All']],
'searching' : true,
'ordering' : true,
'info' : true,
'autoWidth' : false,
'order' : [[0,'desc'], [1,'desc']],
// Parameters // -----------------------------------------------------------------------------
'pageLength' : sessionsRows, // Recurring function to monitor the URL and reinitialize if needed
function deviceSessionsPageUpdater() {
initDeviceSessionsPage();
'columnDefs' : [ // Run updater again after delay
{visible: false, targets: [0]}, setTimeout(deviceSessionsPageUpdater, 200);
}
// Replace HTML codes // start updater
{targets: [3,5], deviceSessionsPageUpdater();
'createdCell': function (td, cellData, rowData, row, col) {
$(td).html (translateHTMLcodes (cellData));
} },
// Date
{targets: [1,2],
"createdCell": function (td, cellData, rowData, row, col) {
// console.log(cellData);
if (!cellData.includes("missing event") && !cellData.includes("..."))
{
if (cellData.includes("+")) { // Check if timezone offset is present
cellData = cellData.split('+')[0]; // Remove timezone offset
}
// console.log(cellData);
result = localizeTimestamp(cellData);
} else
{
result = translateHTMLcodes(cellData)
}
$(td).html (result);
} }
],
// Processing
'processing' : true,
'language' : {
processing: '<table><td width="130px" align="middle"><?= lang("DevDetail_Loading");?></td>'+
'<td><i class="ion ion-ios-loop-strong fa-spin fa-2x fa-fw">'+
'</td></table>',
emptyTable: 'No data',
"lengthMenu": "<?= lang('Events_Tablelenght');?>",
"search": "<?= lang('Events_Searchbox');?>: ",
"paginate": {
"next": "<?= lang('Events_Table_nav_next');?>",
"previous": "<?= lang('Events_Table_nav_prev');?>"
},
"info": "<?= lang('Events_Table_info');?>",
}
});
}
function loadSessionsData(){
$('#tableSessions').DataTable().ajax.url('php/server/events.php?action=getDeviceSessions&mac=' + getMac() +'&period='+ period).load();
}
initializeSessionsDatatable();
loadSessionsData();
</script> </script>

View File

@@ -1047,40 +1047,24 @@ function multiEditDevices()
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Function collects shown devices from the DataTable // Function collects shown devices from the DataTable
function getMacsOfShownDevices() { function getMacsOfShownDevices() {
rows = $('#tableDevices')[0].rows; var table = $('#tableDevices').DataTable();
macs = [];
// var devicesDataTableData = $('#tableDevices').dataTable().fnGetData(); var macs = [];
var devicesDataTableData = $('#tableDevices').DataTable().rows({ selected: false, page: 'current' }).data().toArray();
console.log(devicesDataTableData); // Get all row indexes on current page, in display order
var allIndexes = table.rows({ page: 'current' }).indexes();
var selectedDevices = []; allIndexes.each(function(idx) {
var rowData = table.row(idx).data();
// first row is the heading, skip if (rowData) {
for (var i = 1; i < rows.length; i++) { macs.push(rowData[mapIndx(11)]); // mapIndx(11) == MAC column
var rowIndex = rows[i]._DT_RowIndex;
// Ensure the rowIndex is valid and within bounds of devicesDataTableData
if (rowIndex >= 0 && rowIndex < devicesDataTableData.length) {
selectedDevices.push(devicesDataTableData[rowIndex]);
} else {
console.log(`Invalid rowIndex: ${rowIndex} at row ${i}`);
} }
} });
for (var j = 0; j < selectedDevices.length; j++) {
// Ensure that selectedDevices[j] is not undefined
if (selectedDevices[j]) {
macs.push(selectedDevices[j][mapIndx(11)]); // mapIndx(11) == MAC
} else {
console.log(`selectedDevices[${j}] is undefined`);
}
}
return macs; return macs;
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Handle custom actions/properties on a device // Handle custom actions/properties on a device
function renderCustomProps(custProps, mac) { function renderCustomProps(custProps, mac) {

View File

@@ -1109,46 +1109,52 @@ function getGuid() {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Loading Spinner overlay // Loading Spinner overlay
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
spinnerHtml = `
<!-- spinner -->
<div id="loadingSpinner" style="display: block">
<div class="pa_semitransparent-panel"></div>
<div class="panel panel-default pa_spinner">
<table>
<td width="130px" align="middle">_text_</td>
<td><i class="ion ion-ios-loop-strong fa-spin fa-2x fa-fw"></td>
</table>
</div>
</div>
`
function showSpinner(stringKey='Loading') let spinnerTimeout = null;
{ let animationTime = 300
if(stringKey == "") function showSpinner(stringKey = 'Loading') {
{ const text = isEmpty(stringKey) ? "Loading" : getString(stringKey || "Loading");
text = '' const spinner = $("#loadingSpinner");
} else
{ if (spinner.length && spinner.is(':visible')) {
text = getString(stringKey) clearTimeout(spinnerTimeout);
}
console.log(spinner);
text = isEmpty(text) ? "Loading" : text; $("#loadingSpinnerText").text(text);
spinner.addClass("visible");
if($("#loadingSpinner").length) spinner.fadeIn(animationTime);
{ } else {
$("#loadingSpinner").show(); $("#loadingSpinnerText").text(text);
}
else{ requestAnimationFrame(() => {
$(".wrapper").append(spinnerHtml.replace('_text_',text)) spinner.addClass("visible");
spinner.fadeIn(animationTime);
});
} }
} }
// -----------------------------------------------------------------------------
function hideSpinner() function hideSpinner() {
{ clearTimeout(spinnerTimeout);
$("#loadingSpinner").hide()
const spinner = $("#loadingSpinner");
if (spinner.length) {
spinner.removeClass("visible");
spinner.fadeOut(animationTime);
spinnerTimeout = setTimeout(() => {
spinner.removeClass("visible");
spinner.fadeOut(animationTime); // optional remove or hide again
}, 300);
}
} }
// -------------------------------------------------------- // --------------------------------------------------------
// Calls a backend function to add a front-end event to an execution queue // Calls a backend function to add a front-end event to an execution queue

View File

@@ -136,7 +136,21 @@
<!-- ----------------------------------------------------------------------- --> <!-- ----------------------------------------------------------------------- -->
<!-- Layout Boxed Yellow --> <!-- Layout Boxed Yellow -->
<!-- spinner -->
<body class="hold-transition fixed <?php echo $pia_skin_selected;?> theme-<?php echo $UI_THEME;?> sidebar-mini" onLoad="update_servertime();" > <body class="hold-transition fixed <?php echo $pia_skin_selected;?> theme-<?php echo $UI_THEME;?> sidebar-mini" onLoad="update_servertime();" >
<div id="loadingSpinner">
<div class="pa_semitransparent-panel"></div>
<div class="panel panel-default pa_spinner">
<table>
<td id="loadingSpinnerText" width="130px" ></td>
<td><i class="ion ion-ios-loop-strong fa-spin fa-2x fa-fw"></td>
</table>
</div>
</div>
<!-- Site wrapper --> <!-- Site wrapper -->
<div class="wrapper"> <div class="wrapper">

View File

@@ -9,7 +9,7 @@
"About_Exit": "Sign out", "About_Exit": "Sign out",
"About_Title": "Network security scanner & notification framework", "About_Title": "Network security scanner & notification framework",
"AppEvents_AppEventProcessed": "Processed", "AppEvents_AppEventProcessed": "Processed",
"AppEvents_DateTimeCreated": "Discovered On", "AppEvents_DateTimeCreated": "Logged",
"AppEvents_Extra": "Extra", "AppEvents_Extra": "Extra",
"AppEvents_GUID": "Application Event GUID", "AppEvents_GUID": "Application Event GUID",
"AppEvents_Helper1": "Helper 1", "AppEvents_Helper1": "Helper 1",

View File

@@ -49,10 +49,19 @@ function initMacFilter() {
return mac; return mac;
} }
// -----------------------------------------------
// INIT with polling for panel element visibility
// -----------------------------------------------
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Initializes the fields if the MAC in the URL is different or not yet set // Initializes the fields if the MAC in the URL is different or not yet set
function initFields() { function initFields() {
// Only proceed if .plugin-content is visible
if (!$('.plugin-content:visible').length) {
return; // exit early if nothing is visible
}
// Get current value from the readonly text field // Get current value from the readonly text field
const currentVal = initMacFilter(); const currentVal = initMacFilter();
@@ -74,15 +83,6 @@ function initFields() {
} }
} }
// -----------------------------------------------------------------------------
// Recurring function to monitor the URL and reinitialize if needed
function updater() {
initFields();
// Run updater again after 500 milliseconds
setTimeout(updater, 500);
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Get form control according to the column definition from config.json > database_column_definitions // Get form control according to the column definition from config.json > database_column_definitions
function getFormControl(dbColumnDef, value, index) { function getFormControl(dbColumnDef, value, index) {
@@ -285,11 +285,9 @@ function getData(){
pluginHistory = res["data"]; pluginHistory = res["data"];
generateTabs() generateTabs()
}); });
}); });
}); });
}); });
} }
@@ -301,15 +299,24 @@ function generateTabs() {
// Sort pluginDefinitions by unique_prefix alphabetically // Sort pluginDefinitions by unique_prefix alphabetically
pluginDefinitions.sort((a, b) => a.unique_prefix.localeCompare(b.unique_prefix)); pluginDefinitions.sort((a, b) => a.unique_prefix.localeCompare(b.unique_prefix));
assignActive = true;
// Iterate over the sorted pluginDefinitions to create tab headers and content // Iterate over the sorted pluginDefinitions to create tab headers and content
pluginDefinitions.forEach(pluginObj => { pluginDefinitions.forEach(pluginObj => {
if (pluginObj.show_ui) { if (pluginObj.show_ui) {
stats = createTabContent(pluginObj); // Create the content for each tab stats = createTabContent(pluginObj, assignActive); // Create the content for each tab
createTabHeader(pluginObj, stats); // Create the header for each tab
}
});
hideSpinner() if(stats.objectDataCount > 0)
{
createTabHeader(pluginObj, stats, assignActive); // Create the header for each tab
assignActive = false; // only mark first with content active
}
}
});
hideSpinner()
} }
function resetTabs() { function resetTabs() {
@@ -318,27 +325,30 @@ function resetTabs() {
$('#tabs-content-location').empty(); $('#tabs-content-location').empty();
} }
function createTabHeader(pluginObj, stats) { // ---------------------------------------------------------------
// left headers
function createTabHeader(pluginObj, stats, assignActive) {
const prefix = pluginObj.unique_prefix; // Get the unique prefix for the plugin const prefix = pluginObj.unique_prefix; // Get the unique prefix for the plugin
// Determine the active class for the first tab // Determine the active class for the first tab
const activeClass = pluginDefinitions.indexOf(pluginObj) === 0 ? 'active' : ''; assignActive ? activeClass = "active" : activeClass = "";
if(stats.objectDataCount > 0) // Append the tab header to the tabs location
{ $('#tabs-location').append(`
// Append the tab header to the tabs location <li class="left-nav ${activeClass} ">
$('#tabs-location').append(` <a class="col-sm-12 textOverflow" href="#${prefix}" data-plugin-prefix="${prefix}" id="${prefix}_id" data-toggle="tab">
<li class="left-nav ${activeClass} "> ${getString(`${prefix}_icon`)} ${getString(`${prefix}_display_name`)}
<a class="col-sm-12 textOverflow" href="#${prefix}" data-plugin-prefix="${prefix}" id="${prefix}_id" data-toggle="tab">
${getString(`${prefix}_icon`)} ${getString(`${prefix}_display_name`)} </a>
${stats.objectDataCount > 0 ? `<div class="pluginBadgeWrap"><span title="" class="badge pluginBadge" >${stats.objectDataCount}</span></div>` : ""}
</a> </li>
${stats.objectDataCount > 0 ? `<div class="pluginBadgeWrap"><span title="" class="badge pluginBadge" >${stats.objectDataCount}</span></div>` : ""} `);
</li>
`);
}
} }
function createTabContent(pluginObj) { // ---------------------------------------------------------------
// Content of selected plugin (header)
function createTabContent(pluginObj, assignActive) {
const prefix = pluginObj.unique_prefix; // Get the unique prefix for the plugin const prefix = pluginObj.unique_prefix; // Get the unique prefix for the plugin
const colDefinitions = getColumnDefinitions(pluginObj); // Get column definitions for DataTables const colDefinitions = getColumnDefinitions(pluginObj); // Get column definitions for DataTables
@@ -349,7 +359,7 @@ function createTabContent(pluginObj) {
// Append the content structure for the plugin's tab to the content location // Append the content structure for the plugin's tab to the content location
$('#tabs-content-location').append(` $('#tabs-content-location').append(`
<div id="${prefix}" class="tab-pane ${pluginDefinitions.indexOf(pluginObj) === 0 ? 'active' : ''}"> <div id="${prefix}" class="tab-pane ${objectData.length > 0 && assignActive? 'active' : ''}">
${generateTabNavigation(prefix, objectData.length, eventData.length, historyData.length)} <!-- Create tab navigation --> ${generateTabNavigation(prefix, objectData.length, eventData.length, historyData.length)} <!-- Create tab navigation -->
<div class="tab-content"> <div class="tab-content">
${generateDataTable(prefix, 'Objects', objectData, colDefinitions)} ${generateDataTable(prefix, 'Objects', objectData, colDefinitions)}
@@ -435,7 +445,7 @@ function generateDataTable(prefix, tableType, data, colDefinitions) {
</table> </table>
<div class="plugin-obj-purge"> <div class="plugin-obj-purge">
<button class="btn btn-primary" onclick="purgeAll('${prefix}', 'Plugins_${tableType}' )"><?= lang('Plugins_DeleteAll');?></button> <button class="btn btn-primary" onclick="purgeAll('${prefix}', 'Plugins_${tableType}' )"><?= lang('Plugins_DeleteAll');?></button>
${tableType !== 'Events' ? `<button class="btn btn-danger" onclick="deleteListed('${prefix}', 'Plugins_${tableType}' )"><?= lang('Plugins_Obj_DeleteListed');?></button>` : ''} ${tableType !== 'Events' ? `<button class="btn btn-primary" onclick="deleteListed('${prefix}', 'Plugins_${tableType}' )"><?= lang('Plugins_Obj_DeleteListed');?></button>` : ''}
</div> </div>
</div> </div>
`; `;
@@ -583,14 +593,22 @@ function deleteListedExecute() {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Main sequence // Main sequence
// show spinning icon // -----------------------------------------------------------------------------
showSpinner() // Recurring function to monitor the URL and reinitialize if needed
// Start updater on page load function updater() {
$(document).ready(function () { initFields();
setTimeout(() => {
updater(); // Run updater again after delay
}, 100); setTimeout(updater, 200);
}
});
// if visible, load immediately, if not start updater
if (!$('.plugin-content:visible').length) {
updater();
}
else
{
initFields();
}
</script> </script>