mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-07-25 23:18:06 -04:00
BE+FE: change log
This commit is contained in:
311
front/changeLogCore.php
Normal file
311
front/changeLogCore.php
Normal file
@@ -0,0 +1,311 @@
|
||||
<script>
|
||||
showSpinner();
|
||||
</script>
|
||||
<?php require 'php/templates/skel_change_log.php'; ?>
|
||||
<section class="content">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="box">
|
||||
|
||||
<!-- Filter bar -->
|
||||
<div class="box-header " style="display:flex; gap:10px; flex-wrap:wrap; align-items:center;">
|
||||
<select id="filterChangedBy" class="form-control" style="width:200px;">
|
||||
<option value=""><?= lang('device_history_col_source'); ?> - All</option>
|
||||
</select>
|
||||
<select id="filterChangedColumn" class="form-control" style="width:200px;">
|
||||
<option value=""><?= lang('device_history_col_dropdown'); ?> - All</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- DataTable -->
|
||||
<div class="box-body">
|
||||
<table id="changeHistoryTable" class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?= lang('device_history_col_time'); ?></th>
|
||||
<th><?= lang('device_history_col_source'); ?></th>
|
||||
<th><?= lang('gen_device'); ?></th>
|
||||
<th><?= lang('device_history_table_title_changes'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="changeHistoryBody">
|
||||
<tr><td colspan="4" class="text-center">
|
||||
<i class="fa fa-spinner fa-spin"></i>
|
||||
</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
let historyTable;
|
||||
const urlMac = getMacFromUrl();
|
||||
|
||||
function initializeHistoryTable() {
|
||||
|
||||
historyTable = $('#changeHistoryTable').DataTable({
|
||||
|
||||
processing: true,
|
||||
serverSide: true,
|
||||
paging: true,
|
||||
searching: true,
|
||||
ordering: true,
|
||||
info: true,
|
||||
autoWidth: false,
|
||||
|
||||
pageLength: getSetting("UI_DEFAULT_PAGE_SIZE"),
|
||||
lengthMenu: getLengthMenu(getSetting("UI_DEFAULT_PAGE_SIZE")),
|
||||
|
||||
ajax: function (dtRequest, callback) {
|
||||
|
||||
const limit = parseInt(dtRequest.length, 10) || 50;
|
||||
const start = parseInt(dtRequest.start, 10) || 0;
|
||||
const page = Math.floor(start / limit) + 1;
|
||||
const search = dtRequest.search?.value || null;
|
||||
const order = dtRequest.order?.[0];
|
||||
|
||||
devGUID = null;
|
||||
|
||||
if(urlMac)
|
||||
{
|
||||
devGUID = getDevDataByMac(urlMac,"devGUID")
|
||||
}
|
||||
|
||||
const columns = [
|
||||
"timestamp",
|
||||
"changedBy",
|
||||
"devGUID",
|
||||
null // changes column (not sortable)
|
||||
];
|
||||
|
||||
const sort = [];
|
||||
|
||||
if (order) {
|
||||
const field = columns[order.column];
|
||||
if (field) {
|
||||
sort.push({
|
||||
field: field,
|
||||
order: order.dir
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const query = `
|
||||
query(
|
||||
$devGUID:String,
|
||||
$changedColumn:String,
|
||||
$changedBy:String,
|
||||
$options:PageQueryOptionsInput
|
||||
){
|
||||
deviceHistoryGrouped(
|
||||
devGUID:$devGUID
|
||||
changedColumn:$changedColumn
|
||||
changedBy:$changedBy
|
||||
options:$options
|
||||
){
|
||||
count
|
||||
history{
|
||||
devGUID
|
||||
timestamp
|
||||
changedBy
|
||||
changes{
|
||||
changedColumn
|
||||
oldValue
|
||||
newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
console.log(query)
|
||||
|
||||
$.ajax({
|
||||
url: "/server/graphql",
|
||||
method: "POST",
|
||||
contentType: "application/json",
|
||||
headers: {
|
||||
Authorization: "Bearer " + getSetting("API_TOKEN")
|
||||
},
|
||||
|
||||
data: JSON.stringify({
|
||||
query,
|
||||
variables: {
|
||||
changedBy: $('#filterChangedBy').val() || null,
|
||||
changedColumn: $('#filterChangedColumn').val() || null,
|
||||
devGUID: devGUID,
|
||||
options: {
|
||||
page: page,
|
||||
limit: limit,
|
||||
sort: sort,
|
||||
search: search
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
success: function (resp) {
|
||||
const result = resp?.data?.deviceHistoryGrouped;
|
||||
|
||||
callback({
|
||||
data: result?.history || [],
|
||||
recordsTotal: result?.count || 0,
|
||||
recordsFiltered: result?.count || 0
|
||||
});
|
||||
|
||||
hideSpinner();
|
||||
hideChangeLogSkeleton();
|
||||
},
|
||||
|
||||
error: function () {
|
||||
callback({
|
||||
data: [],
|
||||
recordsTotal: 0,
|
||||
recordsFiltered: 0
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
columns: [
|
||||
{ data: "timestamp" },
|
||||
{ data: "changedBy" },
|
||||
{ data: "devGUID" },
|
||||
{ data: "changes" }
|
||||
],
|
||||
|
||||
columnDefs: [
|
||||
{
|
||||
targets: 0,
|
||||
render: function (data) {
|
||||
return localizeTimestamp(data);
|
||||
}
|
||||
},
|
||||
{
|
||||
targets: 1,
|
||||
render: function (data) {
|
||||
|
||||
return `${escHtml(data)}`;
|
||||
}
|
||||
},
|
||||
{
|
||||
targets: 2,
|
||||
render: function (data) {
|
||||
return `<a href="deviceDetails.php?mac=${getDevDataByGuid(escHtml(data), "devMac")}" data-guid="${escHtml(data)}">${getDevDataByGuid(escHtml(data), "devName")} (${escHtml(data)}) </a>`;
|
||||
}
|
||||
},
|
||||
{
|
||||
targets: 3,
|
||||
orderable: false,
|
||||
searchable: false,
|
||||
render: function (changes) {
|
||||
|
||||
if (!Array.isArray(changes)) return "";
|
||||
|
||||
return changes.map(c =>
|
||||
`<div>
|
||||
<code>${escHtml(c.changedColumn)}</code>:
|
||||
<span class="text-danger">${escHtml(c.oldValue || "∅")}</span>
|
||||
→
|
||||
<span class="text-success">${escHtml(c.newValue || "∅")}</span>
|
||||
</div>`
|
||||
).join("");
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
language: {
|
||||
processing: '<i class="fa fa-spinner fa-spin"></i>',
|
||||
emptyTable: "<?= lang('device_history_empty_state'); ?>"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function bindHistoryFilters() {
|
||||
|
||||
$('#filterChangedBy, #filterChangedColumn').on('change', function () {
|
||||
if (historyTable) {
|
||||
showSpinner();
|
||||
historyTable.ajax.reload(null, true); // reset paging
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function escHtml(str) {
|
||||
if (str === null || str === undefined) return '';
|
||||
return String(str)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"');
|
||||
}
|
||||
|
||||
function populateFilters() {
|
||||
var query = `
|
||||
query {
|
||||
allDeviceHistoryGrouped(limit: 1) {
|
||||
history { changedBy changes { changedColumn } }
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
url = '/server/devices/history/filters';
|
||||
|
||||
if(urlMac)
|
||||
{
|
||||
url = `${url}?devGUID=${getDevDataByMac(urlMac,"devGUID")}`;
|
||||
}
|
||||
|
||||
// Fetch distinct filter values from the filter-values endpoint
|
||||
$.ajax({
|
||||
url: url,
|
||||
method: 'GET',
|
||||
headers: { 'Authorization': 'Bearer ' + getSetting('API_TOKEN') },
|
||||
success: function(resp) {
|
||||
if (!resp.data) return;
|
||||
|
||||
var byEl = document.getElementById('filterChangedBy');
|
||||
var colEl = document.getElementById('filterChangedColumn');
|
||||
|
||||
(resp.data.changedBy || []).forEach(function(v) {
|
||||
byEl.add(new Option(v, v));
|
||||
});
|
||||
|
||||
(resp.data.changedColumn || []).forEach(function(v) {
|
||||
colEl.add(new Option(v, v));
|
||||
});
|
||||
|
||||
// trigger initial load AFTER filters are ready
|
||||
$('#changeHistoryTable').DataTable().ajax.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getMacFromUrl() {
|
||||
return new URLSearchParams(window.location.search).get('mac');
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
callAfterAppInitialized(initChangeLog);
|
||||
});
|
||||
|
||||
function initChangeLog(){
|
||||
populateFilters();
|
||||
initializeHistoryTable();
|
||||
bindHistoryFilters();
|
||||
}
|
||||
|
||||
function hideChangeLogSkeleton() {
|
||||
$('#change-log-skeleton').fadeOut(0, function() { $(this).hide(); });
|
||||
}
|
||||
|
||||
function showChangeLogSkeleton() {
|
||||
var $skel = $('#change-log-skeleton');
|
||||
$skel.stop(true, true).fadeIn(0);
|
||||
}
|
||||
|
||||
</script>
|
||||
Reference in New Issue
Block a user