mirror of
https://github.com/itflow-org/itflow
synced 2026-08-31 03:45:13 +00:00
Use Datatables for User Activity on the client portal
This commit is contained in:
@@ -4,6 +4,10 @@
|
||||
* Everything this contact has done in the portal, and every sign-in
|
||||
*/
|
||||
|
||||
// Read by client/includes/header.php and footer.php - see the note there.
|
||||
// Must be set before inc_all.php, which pulls the header in.
|
||||
$portal_load_datatables = true;
|
||||
|
||||
header("Content-Security-Policy: default-src 'self'");
|
||||
|
||||
require_once "includes/inc_all.php";
|
||||
@@ -13,33 +17,26 @@ require_once "includes/inc_all.php";
|
||||
* not a section of the portal. Scoped on log_user_id - the portal user this
|
||||
* contact signs in as - so an agent working this client never appears here,
|
||||
* and on log_client_id as a second fence.
|
||||
*
|
||||
* DataTables searches and paginates in the browser, so the whole set is sent at
|
||||
* once rather than a page at a time. The cap is there so that a contact with
|
||||
* years of history cannot turn this page into a several-megabyte document; it
|
||||
* is generous enough that nobody normal will meet it, and the note below says
|
||||
* so plainly when they do.
|
||||
*/
|
||||
$page = intval($_GET['page'] ?? 1);
|
||||
if ($page < 1) {
|
||||
$page = 1;
|
||||
}
|
||||
|
||||
$records_per_page = 25;
|
||||
$offset = ($page - 1) * $records_per_page;
|
||||
$activity_limit = 1000;
|
||||
|
||||
$log_scope = "log_user_id = $session_user_id AND log_client_id = $session_client_id";
|
||||
|
||||
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(log_id) AS total FROM logs WHERE $log_scope"));
|
||||
$total_records = intval($row['total']);
|
||||
$total_pages = (int) ceil($total_records / $records_per_page);
|
||||
|
||||
// A page number past the end would show nothing at all with no way back
|
||||
if ($total_pages > 0 && $page > $total_pages) {
|
||||
$page = $total_pages;
|
||||
$offset = ($page - 1) * $records_per_page;
|
||||
}
|
||||
|
||||
$sql_activity = mysqli_query(
|
||||
$mysqli,
|
||||
"SELECT log_action, log_created_at, log_description, log_ip, log_type FROM logs
|
||||
WHERE $log_scope
|
||||
ORDER BY log_id DESC
|
||||
LIMIT $records_per_page OFFSET $offset"
|
||||
LIMIT $activity_limit"
|
||||
);
|
||||
|
||||
?>
|
||||
@@ -59,10 +56,15 @@ $sql_activity = mysqli_query(
|
||||
|
||||
<?php } else { ?>
|
||||
|
||||
<table class="table table-bordered border border-dark">
|
||||
<table class="table table-striped table-bordered border border-dark dataTables" style="width:100%">
|
||||
<thead class="table-dark">
|
||||
<tr>
|
||||
<th>When</th>
|
||||
<?php /* Ordering off on this column deliberately. The cell reads
|
||||
"Today at 4:12 PM", and sorting that as text puts October
|
||||
before September and Today next to Tomorrow. The rows
|
||||
arrive newest-first from SQL and the initialiser keeps
|
||||
that order, so the useful sort is the one already applied. */ ?>
|
||||
<th data-dt-order="disable">When</th>
|
||||
<th>Type</th>
|
||||
<th>What happened</th>
|
||||
<th>From</th>
|
||||
@@ -107,25 +109,11 @@ $sql_activity = mysqli_query(
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<?php if ($total_pages > 1) { ?>
|
||||
<div class="row align-items-center">
|
||||
<div class="col-sm">
|
||||
<p class="text-muted mb-0">
|
||||
Page <strong><?= $page ?></strong> of <strong><?= $total_pages ?></strong>
|
||||
— <strong><?= $total_records ?></strong> records
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<ul class="pagination justify-content-sm-end mb-0">
|
||||
<li class="page-item <?= $page <= 1 ? 'disabled' : '' ?>">
|
||||
<a class="page-link" href="?page=<?= $page - 1 ?>">Previous</a>
|
||||
</li>
|
||||
<li class="page-item <?= $page >= $total_pages ? 'disabled' : '' ?>">
|
||||
<a class="page-link" href="?page=<?= $page + 1 ?>">Next</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<?php if ($total_records > $activity_limit) { ?>
|
||||
<small class="text-muted">
|
||||
Showing your most recent <?= $activity_limit ?> records of <?= $total_records ?>.
|
||||
Raise a ticket if you need to go back further.
|
||||
</small>
|
||||
<?php } ?>
|
||||
|
||||
<?php } ?>
|
||||
|
||||
@@ -74,6 +74,11 @@
|
||||
<script src="/js/phone_inputs.js"></script>
|
||||
<?php } ?>
|
||||
|
||||
<?php if (!empty($portal_load_datatables)) { ?>
|
||||
<script src="/libs/DataTables/datatables.min.js"></script>
|
||||
<script src="/js/portal_datatables.js"></script>
|
||||
<?php } ?>
|
||||
|
||||
<script src="/js/keepalive.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
@@ -47,6 +47,10 @@ header("X-Frame-Options: DENY"); // Legacy
|
||||
<link rel="stylesheet" href="/libs/intl-tel-input/css/intlTelInput.min.css">
|
||||
<?php } ?>
|
||||
|
||||
<?php if (!empty($portal_load_datatables)) { ?>
|
||||
<link rel="stylesheet" href="/libs/DataTables/datatables.min.css">
|
||||
<?php } ?>
|
||||
|
||||
</head>
|
||||
|
||||
<?php /* Same country bridge as the agent header: intl-tel-input wants an ISO2
|
||||
|
||||
38
js/portal_datatables.js
Normal file
38
js/portal_datatables.js
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* ITFlow - DataTables for the client portal.
|
||||
*
|
||||
* The agent side initialises DataTables from js/app.js, which the portal cannot
|
||||
* load: app.js calls TinyMCE, Tom Select, Flatpickr and IMask unconditionally
|
||||
* and none of them exist here. This is the portal's own one-liner rather than a
|
||||
* shared extraction, because unlike the phone-input logic there is no real
|
||||
* behaviour to keep in step - just a constructor.
|
||||
*
|
||||
* A separate file, not an inline <script>, because portal pages send
|
||||
* Content-Security-Policy: default-src 'self', which blocks inline script.
|
||||
*/
|
||||
function initPortalDataTables() {
|
||||
if (typeof DataTable !== 'function') {
|
||||
return;
|
||||
}
|
||||
|
||||
document.querySelectorAll('table.dataTables').forEach(function (el) {
|
||||
if (DataTable.isDataTable(el)) {
|
||||
return;
|
||||
}
|
||||
|
||||
new DataTable(el, {
|
||||
// The server already ordered these rows - newest first for an
|
||||
// activity log. DataTables' default is to re-sort on column 0
|
||||
// ascending, which would silently reverse that.
|
||||
order: [],
|
||||
pageLength: 25,
|
||||
lengthMenu: [10, 25, 50, 100]
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initPortalDataTables);
|
||||
} else {
|
||||
initPortalDataTables();
|
||||
}
|
||||
Reference in New Issue
Block a user