Upload files to "system/plugin/ui"
Signed-off-by: nestict <icttechnest@gmail.com>
This commit is contained in:
parent
3a49bf9afa
commit
717cb65516
943
system/plugin/ui/pppoe_monitor.tpl
Normal file
943
system/plugin/ui/pppoe_monitor.tpl
Normal file
@ -0,0 +1,943 @@
|
||||
{include file="sections/header.tpl"}
|
||||
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
||||
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
|
||||
<script src="https://cdn.datatables.net/1.10.23/js/jquery.dataTables.min.js"></script>
|
||||
<link rel="stylesheet" href="https://cdn.datatables.net/1.10.23/css/jquery.dataTables.min.css">
|
||||
<link rel="stylesheet" href="https://cdn.datatables.net/buttons/1.7.1/css/buttons.dataTables.min.css">
|
||||
<script src="https://cdn.datatables.net/buttons/1.7.1/js/dataTables.buttons.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<script src="https://cdn.datatables.net/1.11.3/js/jquery.dataTables.min.js"></script>
|
||||
<script src="https://cdn.datatables.net/1.11.3/js/dataTables.bootstrap5.min.js"></script>
|
||||
|
||||
<style>
|
||||
.modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
.modal-content {
|
||||
background-color: #fefefe;
|
||||
margin: 15% auto;
|
||||
padding: 0px;
|
||||
border: 1px solid #888;
|
||||
width: 80%;
|
||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
||||
max-width: 600px;
|
||||
}
|
||||
.close {
|
||||
color: #aaa;
|
||||
float: right;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
.close:hover,
|
||||
.close:focus {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
.card-body {
|
||||
padding: 1rem;
|
||||
}
|
||||
.card-header {
|
||||
padding: .75rem 1.25rem;
|
||||
margin-bottom: 0;
|
||||
background-color: none;
|
||||
border-bottom: 1px solid rgba(0,0,0,.125);
|
||||
}
|
||||
.card-title {
|
||||
margin-bottom: .75rem;
|
||||
}
|
||||
.form-group {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.table-responsive {
|
||||
display: block;
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
-ms-overflow-style: -ms-autohiding-scrollbar;
|
||||
}
|
||||
.table {
|
||||
width: 100%;
|
||||
margin-bottom: 1rem;
|
||||
color: #212529;
|
||||
}
|
||||
.container {
|
||||
padding-top: 20px;
|
||||
}
|
||||
#ppp-table_wrapper {
|
||||
padding: 15px;
|
||||
}
|
||||
#ppp-table th, #ppp-table td {
|
||||
text-align: center;
|
||||
padding: 6px;
|
||||
}
|
||||
.table-striped tbody tr:nth-of-type(odd) {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
.panel-default {
|
||||
border-color: #ddd;
|
||||
}
|
||||
.panel-heading {
|
||||
background-color: #f5f5f5;
|
||||
border-color: #ddd;
|
||||
}
|
||||
.nav-tabs {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.nav-tabs > li > a {
|
||||
border-radius: 0;
|
||||
color: #555;
|
||||
background-color: #f9f9f9;
|
||||
border-color: #ddd;
|
||||
}
|
||||
.nav-tabs > li.active > a,
|
||||
.nav-tabs > li.active > a:focus,
|
||||
.nav-tabs > li.active > a:hover {
|
||||
background-color: #fff;
|
||||
color: #333;
|
||||
border: 1px solid #ddd;
|
||||
border-bottom-color: transparent;
|
||||
cursor: default;
|
||||
}
|
||||
.table-striped tbody tr:nth-of-type(odd) {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
.table th {
|
||||
background-color: #f5f5f5;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
padding: 8px;
|
||||
}
|
||||
.table-striped > tbody > tr > td {
|
||||
background-color: #fff;
|
||||
}
|
||||
.status-connect {
|
||||
color: #5cb85c;
|
||||
}
|
||||
.status-disconnect {
|
||||
color: #d9534f;
|
||||
}
|
||||
.modalsupport {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
background-color: rgb(0,0,0);
|
||||
background-color: rgba(0,0,0,0.4);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.modalsupport-content {
|
||||
background-color: #fefefe;
|
||||
margin: auto;
|
||||
padding: 0px;
|
||||
border: 1px solid #888;
|
||||
width: 80%;
|
||||
max-width: 500px;
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
|
||||
text-align: center;
|
||||
}
|
||||
.modalsupport-close {
|
||||
color: #aaa;
|
||||
float: right;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.modalsupport-close:hover,
|
||||
.modalsupport-close:focus {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
.card {
|
||||
border: none;
|
||||
}
|
||||
.card-header {
|
||||
background-color: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
.card-body {
|
||||
padding: 20px;
|
||||
}
|
||||
.donate-button {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.modalsupport img {
|
||||
width: 100px;
|
||||
height: auto;
|
||||
margin-top: 15px;
|
||||
}
|
||||
.dataSize {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.action-icons i {
|
||||
cursor: pointer;
|
||||
margin-right: 10px;
|
||||
color: #007bff;
|
||||
}
|
||||
.action-icons i:hover {
|
||||
color: #0056b3;
|
||||
}
|
||||
.modal-title {
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
display: block;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.table-bordered {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
table-layout: fixed;
|
||||
}
|
||||
.table-bordered th, .table-bordered td {
|
||||
width: auto;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 5px;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
}
|
||||
.advanced-search-container {
|
||||
margin-bottom: 20px;
|
||||
padding: 15px;
|
||||
background-color: #f9f9f9;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
.form-inline .form-group {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.dataTables_filter {
|
||||
display: none;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.panel-default {
|
||||
padding: 10px;
|
||||
margin: 0;
|
||||
}
|
||||
.panel-heading {
|
||||
padding: 5px 15px;
|
||||
}
|
||||
.panel-body {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
.table th, .table td {
|
||||
font-size: 15px; /* Mengurangi ukuran font pada tabel */
|
||||
}
|
||||
}
|
||||
.traffic-icon {
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
margin-right: 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.traffic-icon-green {
|
||||
background-color: green;
|
||||
}
|
||||
.traffic-icon-yellow {
|
||||
background-color: yellow;
|
||||
}
|
||||
.traffic-icon-red {
|
||||
background-color: red;
|
||||
}
|
||||
.text-left {
|
||||
text-align: left !important;
|
||||
}
|
||||
</style>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
|
||||
<!-- Form dan navigasi tabs -->
|
||||
<form class="form-horizontal" method="post" role="form" action="{$_url}plugin/pppoe">
|
||||
<div class="form-group">
|
||||
<label for="routerSelect" class="col-sm-2 control-label">Select Router</label>
|
||||
<div class="col-sm-10">
|
||||
<select id="routerSelect" name="router" class="form-control" onchange="window.location.href=this.value;">
|
||||
{foreach $routers as $r}
|
||||
<option value="{$_url}plugin/pppoe/{$r['id']}" {if $r['id'] == $router}selected{/if}>
|
||||
{$r['name']}
|
||||
</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="advanced-search-container">
|
||||
<form id="advancedSearchForm" class="form-inline">
|
||||
<div class="form-group">
|
||||
<label for="searchUsername">Username:</label>
|
||||
<input type="text" class="form-control" id="searchUsername" placeholder="Enter username">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="searchStatus">Status:</label>
|
||||
<select class="form-control" id="searchStatus">
|
||||
<option value="">Any</option>
|
||||
<option value="Connected">Connected</option>
|
||||
<option value="Disconnected">Disconnected</option>
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="table-responsive">
|
||||
<div class="panel-body">
|
||||
<table class="table table-striped" id="ppp-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Username</th>
|
||||
<th>IP Address</th>
|
||||
<th>Uptime</th>
|
||||
<th>Service</th>
|
||||
<th>Caller ID</th>
|
||||
<th>Device</th>
|
||||
<th>Download</th>
|
||||
<th>Upload</th>
|
||||
<th>Total Usage</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- Isi tabel akan dimasukkan melalui JavaScript -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="detailsModal" class="modal">
|
||||
<div class="modal-content">
|
||||
<span class="close">×</span>
|
||||
<div class="container-fluid mt-5">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="modal-title">Data Usage for <span id="modalUsername"></span></h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive mt-4">
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<input type="hidden" id="interface" value="">
|
||||
<th id="tabletx"><i class="fa fa-download"></i></th>
|
||||
<th id="tablerx"><i class="fa fa-upload"></i></th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
<div id="chart" class="mt-3"></div>
|
||||
<div id="dailyChart" class="mt-3"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var $j = jQuery.noConflict();
|
||||
|
||||
$j(document).ready(function() {
|
||||
var table = $j('#ppp-table').DataTable({
|
||||
responsive: true,
|
||||
columns: [
|
||||
{ data: 'id', visible: false },
|
||||
{
|
||||
data: 'username',
|
||||
className: 'text-left',
|
||||
render: function(data, type, row) {
|
||||
return '<div style="width: 150px;"><i class="traffic-icon traffic-icon-green"></i> ' + data + '</div>';
|
||||
}
|
||||
},
|
||||
{ data: 'address' },
|
||||
{ data: 'uptime' },
|
||||
{ data: 'service' },
|
||||
{ data: 'caller_id' },
|
||||
{ data: 'manufacturer' }, // New column for device/manufacturer
|
||||
{ data: 'tx', className: 'dataSize' },
|
||||
{ data: 'rx', className: 'dataSize' },
|
||||
{ data: 'total', className: 'dataSize' },
|
||||
{
|
||||
data: 'status',
|
||||
render: function(data) {
|
||||
if (data === 'Connected') {
|
||||
return '<small class="label bg-green">Connected</small>';
|
||||
} else if (data === 'Disconnected') {
|
||||
return '<small class="label bg-red">Disconnected</small>';
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
data: null,
|
||||
render: function(data, type, row) {
|
||||
return '<div class="action-icons" style="display: flex; align-items: center;">' +
|
||||
'<i class="fa fa-area-chart view-details" style="color: blue; cursor: pointer;" title="View Traffic" data-username="' + row.username + '" data-id="' + row.id + '"></i> ' +
|
||||
'<i class="fa fa-retweet reconnect-button" style="color: red; cursor: pointer;" title="Reconnect" data-username="' + row.username + '" data-id="' + row.id + '"></i> ' +
|
||||
'<button class="btn btn-sm ' + (row.is_bound ? 'btn-danger' : 'btn-success') + ' bind-button" data-username="' + row.username + '" data-caller-id="' + row.caller_id + '" data-is-bound="' + row.is_bound + '">' +
|
||||
(row.is_bound ? 'Unbind' : 'Bind') + '</button>' +
|
||||
'</div>';
|
||||
}
|
||||
}
|
||||
],
|
||||
order: [[0, 'asc']],
|
||||
pageLength: 10,
|
||||
lengthMenu: [[10, 25, 50, -1], [10, 25, 50, 'All']],
|
||||
dom: 'Bfrtip',
|
||||
buttons: ['reset', 'pageLength'],
|
||||
paging: true,
|
||||
info: true,
|
||||
searching: true,
|
||||
ajax: {
|
||||
url: '{$_url}plugin/pppoe_get_combined_users/{$router}',
|
||||
dataSrc: ''
|
||||
}
|
||||
});
|
||||
|
||||
// Fungsi untuk mendapatkan batas maksimum
|
||||
function getMaxLimit(data) {
|
||||
if (data.hasOwnProperty('max_limit')) {
|
||||
return data.max_limit.toString();
|
||||
} else {
|
||||
return 'N/A';
|
||||
}
|
||||
}
|
||||
|
||||
// Handle view details icon clicks
|
||||
$j('#ppp-table tbody').on('click', '.view-details', function(e) {
|
||||
e.preventDefault();
|
||||
var username = $j(this).data('username');
|
||||
var id = $j(this).data('id');
|
||||
|
||||
viewDetails(id, username);
|
||||
});
|
||||
|
||||
// Handle reconnect icon clicks
|
||||
$j('#ppp-table tbody').on('click', '.reconnect-button', function(e) {
|
||||
e.preventDefault();
|
||||
var username = $j(this).data('username');
|
||||
var id = $j(this).data('id');
|
||||
|
||||
reconnect(id, username);
|
||||
});
|
||||
|
||||
// Function to handle view details
|
||||
function viewDetails(id, username) {
|
||||
console.log("Viewing details for:", username);
|
||||
$j('#modalUsername').text(username);
|
||||
|
||||
$j.ajax({
|
||||
url: '{$_url}plugin/pppoe_get_combined_users',
|
||||
method: 'GET',
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
var user = response.find(function(item) {
|
||||
return (item.username && item.username.toString().toLowerCase() === username.toString().toLowerCase());
|
||||
});
|
||||
|
||||
if (username !== null && user !== null && user.username !== null) {
|
||||
var interfaceValue = '<pppoe-' + user.username + '>';
|
||||
$j('#interface').val(interfaceValue);
|
||||
$j('#selectedInterface').text(interfaceValue);
|
||||
$j('#detailsModal').css('display', 'block');
|
||||
createChart();
|
||||
createDailyChart(username); // Pass the username to createDailyChart
|
||||
} else {
|
||||
alert('User not found.');
|
||||
}
|
||||
},
|
||||
error: function(xhr, textStatus, errorThrown) {
|
||||
alert('Failed to retrieve user data.');
|
||||
console.error('AJAX error:', textStatus, errorThrown);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Function to handle reconnect
|
||||
function reconnect(id, username) {
|
||||
if (confirm('Are you sure you want to disconnect user ' + username + '?')) {
|
||||
$.ajax({
|
||||
url: '{$_url}plugin/pppoe_monitor_router_delete_ppp_user/{$router}', // Perbaiki URL AJAX
|
||||
method: 'POST',
|
||||
data: { id: id, username: username },
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
alert('User ' + username + ' has been disconnected.');
|
||||
setTimeout(function() {
|
||||
table.ajax.reload();
|
||||
}, 2000);
|
||||
} else {
|
||||
alert('Failed to disconnect user ' + username + ': ' + (response.message || 'Unknown error'));
|
||||
}
|
||||
},
|
||||
error: function(xhr, textStatus, errorThrown) {
|
||||
alert('Failed to disconnect user ' + username + ': ' + (errorThrown || 'Unknown error'));
|
||||
console.error('AJAX error:', textStatus, errorThrown);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Close modal on click of close button
|
||||
$j('.close').click(function() {
|
||||
$j('#detailsModal').css('display', 'none');
|
||||
});
|
||||
|
||||
// Close modal on click outside the modal
|
||||
$j(window).click(function(event) {
|
||||
if (event.target == document.getElementById('detailsModal')) {
|
||||
$j('#detailsModal').css('display', 'none');
|
||||
}
|
||||
});
|
||||
|
||||
// Handle advanced search form submission
|
||||
$j(document).ready(function() {
|
||||
$j('#advancedSearchForm').on('submit', function(e) {
|
||||
e.preventDefault(); // Mencegah pengiriman form secara default
|
||||
|
||||
// Mendapatkan nilai dari input
|
||||
var username = $j('#searchUsername').val();
|
||||
var status = $j('#searchStatus').val();
|
||||
|
||||
// Melakukan pencarian dan menggambar ulang tabel
|
||||
table.column(1).search(username).draw(); // Kolom 1 untuk username
|
||||
table.column(9).search(status).draw(); // Kolom 9 untuk status
|
||||
});
|
||||
|
||||
// Menambahkan ikon search ke dalam tombol
|
||||
var searchButton = $j('<button type="submit" class="btn btn-primary"><i class="fas fa-search"></i></button>');
|
||||
$j('#advancedSearchForm').append(searchButton);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
var chart;
|
||||
var chartData = {
|
||||
txData: [],
|
||||
rxData: []
|
||||
};
|
||||
|
||||
function createChart() {
|
||||
var options = {
|
||||
chart: {
|
||||
height: 350,
|
||||
type: 'area',
|
||||
animations: {
|
||||
enabled: true,
|
||||
easing: 'linear',
|
||||
speed: 200,
|
||||
animateGradually: {
|
||||
enabled: true,
|
||||
delay: 150
|
||||
},
|
||||
dynamicAnimation: {
|
||||
enabled: true,
|
||||
speed: 200
|
||||
}
|
||||
},
|
||||
events: {
|
||||
mounted: function() {
|
||||
updateTrafficValues();
|
||||
setInterval(updateTrafficValues, 3000);
|
||||
}
|
||||
}
|
||||
},
|
||||
stroke: {
|
||||
curve: 'smooth'
|
||||
},
|
||||
series: [
|
||||
{ name: 'Download', data: chartData.txData },
|
||||
{ name: 'Upload', data: chartData.rxData }
|
||||
],
|
||||
xaxis: {
|
||||
type: 'datetime',
|
||||
labels: {
|
||||
formatter: function(value) {
|
||||
return new Date(value).toLocaleTimeString();
|
||||
}
|
||||
}
|
||||
},
|
||||
yaxis: {
|
||||
title: {
|
||||
text: 'Real Time Data Usage'
|
||||
},
|
||||
labels: {
|
||||
formatter: function(value) {
|
||||
return formatBytes(value);
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
x: {
|
||||
format: 'HH:mm:ss'
|
||||
},
|
||||
y: {
|
||||
formatter: function(value) {
|
||||
return formatBytes(value) + 'ps';
|
||||
}
|
||||
}
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
formatter: function(value) {
|
||||
return formatBytes(value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
chart = new ApexCharts(document.querySelector("#chart"), options);
|
||||
chart.render();
|
||||
}
|
||||
|
||||
var dailyChart; // Declare dailyChart variable globally
|
||||
|
||||
function createDailyChart(username) {
|
||||
var currentDate = new Date();
|
||||
var startOfMonth = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1).getTime();
|
||||
var endOfMonth = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0).getTime();
|
||||
|
||||
generateDailyData(username)
|
||||
.then(dailyData => {
|
||||
var dailyTotals = dailyData.download.map((item, index) => ({
|
||||
x: item.x,
|
||||
y: item.y + dailyData.upload[index].y
|
||||
}));
|
||||
|
||||
if (dailyChart) {
|
||||
dailyChart.destroy();
|
||||
}
|
||||
|
||||
var options = {
|
||||
chart: {
|
||||
height: 350,
|
||||
type: 'bar',
|
||||
animations: {
|
||||
enabled: true,
|
||||
easing: 'linear',
|
||||
speed: 800,
|
||||
animateGradually: {
|
||||
enabled: true,
|
||||
delay: 150
|
||||
},
|
||||
dynamicAnimation: {
|
||||
enabled: true,
|
||||
speed: 200
|
||||
}
|
||||
},
|
||||
toolbar: {
|
||||
show: true,
|
||||
}
|
||||
},
|
||||
plotOptions: {
|
||||
bar: {
|
||||
horizontal: false,
|
||||
columnWidth: '15%',
|
||||
endingShape: 'rounded'
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false
|
||||
},
|
||||
stroke: {
|
||||
show: true,
|
||||
width: 2,
|
||||
colors: ['transparent']
|
||||
},
|
||||
series: [{
|
||||
name: 'Download',
|
||||
data: dailyData.upload
|
||||
}, {
|
||||
name: 'Upload',
|
||||
data: dailyData.download
|
||||
}, {
|
||||
name: 'Daily Totals',
|
||||
data: dailyTotals
|
||||
}],
|
||||
xaxis: {
|
||||
type: 'datetime',
|
||||
min: startOfMonth,
|
||||
max: endOfMonth,
|
||||
labels: {
|
||||
formatter: function(value) {
|
||||
return new Date(value).toLocaleDateString();
|
||||
}
|
||||
}
|
||||
},
|
||||
yaxis: {
|
||||
title: {
|
||||
text: 'Total Usage'
|
||||
},
|
||||
labels: {
|
||||
formatter: function(value) {
|
||||
return formatBytesPerSecond(value);
|
||||
}
|
||||
}
|
||||
},
|
||||
fill: {
|
||||
opacity: 1
|
||||
},
|
||||
tooltip: {
|
||||
y: {
|
||||
formatter: function(val) {
|
||||
return formatBytes(val);
|
||||
}
|
||||
}
|
||||
},
|
||||
responsive: [
|
||||
{
|
||||
breakpoint: 480,
|
||||
options: {
|
||||
plotOptions: {
|
||||
bar: {
|
||||
columnWidth: '100%'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
dailyChart = new ApexCharts(document.querySelector("#dailyChart"), options);
|
||||
dailyChart.render();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Failed to fetch daily usage data:", error);
|
||||
});
|
||||
}
|
||||
|
||||
// ========================================== NEW FITUR ==========================================//
|
||||
function generateDailyData(username, startDate, endDate) {
|
||||
return new Promise((resolve, reject) => {
|
||||
$j.ajax({
|
||||
url: '{$_url}plugin/pppoe_monitor_router_daily_data_usage/{$router}',
|
||||
data: {
|
||||
username: username,
|
||||
start_date: startDate,
|
||||
end_date: endDate
|
||||
},
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
console.log("Raw data from server for username", username, ":", data);
|
||||
|
||||
var dailyData = {
|
||||
download: [],
|
||||
upload: []
|
||||
};
|
||||
|
||||
// Iterate over dates in data and find the correct user data
|
||||
for (var date in data) {
|
||||
var users = data[date].users;
|
||||
|
||||
// Handle username as number case
|
||||
var userData = users.find(user => user.username === username || user.username == parseInt(username));
|
||||
|
||||
if (userData) {
|
||||
var rxBytes = convertToBytes(userData.rx);
|
||||
var txBytes = convertToBytes(userData.tx);
|
||||
|
||||
// Store data in dailyData based on date
|
||||
dailyData.download.push({ x: new Date(date).getTime(), y: rxBytes });
|
||||
dailyData.upload.push({ x: new Date(date).getTime(), y: txBytes });
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Filtered daily data for username", username, ":", dailyData);
|
||||
resolve(dailyData);
|
||||
},
|
||||
error: function(xhr, textStatus, errorThrown) {
|
||||
console.error("AJAX Error in generateDailyData:", textStatus, errorThrown);
|
||||
console.log("Status:", xhr.status);
|
||||
console.log("Response Text:", xhr.responseText);
|
||||
reject(errorThrown);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function convertToBytes(value) {
|
||||
let [number, unit] = value.split(' ');
|
||||
number = parseFloat(number);
|
||||
switch (unit) {
|
||||
case 'GB':
|
||||
return number * 1024 * 1024 * 1024;
|
||||
case 'MB':
|
||||
return number * 1024 * 1024;
|
||||
case 'KB':
|
||||
return number * 1024;
|
||||
default:
|
||||
return number;
|
||||
}
|
||||
}
|
||||
// ========================================== NEW FITUR ==========================================//
|
||||
function formatBytesPerSecond(bytes) {
|
||||
if (bytes === 0) {
|
||||
return '0 Bps';
|
||||
}
|
||||
var k = 1024;
|
||||
var sizes = ['Bps', 'KBps', 'MBps', 'GBps', 'TBps', 'PBps', 'EBps', 'ZBps', 'YBps'];
|
||||
var i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
var formattedValue = parseFloat((bytes / Math.pow(k, i)).toFixed(2));
|
||||
return formattedValue + ' ' + sizes[i];
|
||||
}
|
||||
|
||||
// Fungsi untuk mengubah ukuran dalam byte menjadi format yang lebih mudah dibaca
|
||||
function formatBytes(bytes, decimals = 2) {
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
const k = 1024;
|
||||
const dm = decimals < 0 ? 0 : decimals;
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
|
||||
}
|
||||
|
||||
// Function to update traffic values and icons
|
||||
function updateTrafficValues() {
|
||||
var username = $j('#modalUsername').text().trim();
|
||||
var interfaceValue = $j('#interface').val();
|
||||
|
||||
if (!username || !interfaceValue) {
|
||||
console.error("Username or interface is undefined or empty.");
|
||||
return;
|
||||
}
|
||||
|
||||
$j.ajax({
|
||||
url: '{$_url}plugin/pppoe_monitor_router_traffic/{$router}',
|
||||
dataType: 'json',
|
||||
data: { username: username, interface: interfaceValue },
|
||||
success: function(data) {
|
||||
var timestamp = new Date().getTime();
|
||||
var txData = parseInt(data.rows.tx[0]) || 0;
|
||||
var rxData = parseInt(data.rows.rx[0]) || 0;
|
||||
|
||||
// Log data tx dan rx untuk debugging
|
||||
console.log('txData:', txData, 'rxData:', rxData);
|
||||
|
||||
// Update chart data
|
||||
chartData.txData.push({ x: timestamp, y: txData });
|
||||
chartData.rxData.push({ x: timestamp, y: rxData });
|
||||
|
||||
var maxDataPoints = 10;
|
||||
if (chartData.txData.length > maxDataPoints) {
|
||||
chartData.txData.shift();
|
||||
chartData.rxData.shift();
|
||||
}
|
||||
|
||||
// Update series on the chart
|
||||
chart.updateSeries([
|
||||
{ name: 'Download', data: chartData.txData },
|
||||
{ name: 'Upload', data: chartData.rxData }
|
||||
]);
|
||||
|
||||
// Find the icon element for the specific user based on username
|
||||
var userRow = $j('#ppp-table tbody tr').filter(function() {
|
||||
return $j(this).find('td').eq(1).text().trim() === username;
|
||||
});
|
||||
|
||||
var iconElement = userRow.find('.traffic-icon');
|
||||
|
||||
// Define thresholds for traffic levels
|
||||
var thresholdHigh = 2000; // Adjust these values as needed
|
||||
var thresholdMedium = 1500; // Adjust these values as needed
|
||||
|
||||
// Adjust icon color based on traffic levels
|
||||
if (txData > thresholdHigh || rxData > thresholdHigh) {
|
||||
iconElement.removeClass('traffic-icon-green traffic-icon-yellow').addClass('traffic-icon-red');
|
||||
} else if (txData > thresholdMedium || rxData > thresholdMedium) {
|
||||
iconElement.removeClass('traffic-icon-green traffic-icon-red').addClass('traffic-icon-yellow');
|
||||
} else {
|
||||
iconElement.removeClass('traffic-icon-yellow traffic-icon-red').addClass('traffic-icon-green');
|
||||
}
|
||||
},
|
||||
error: function(xhr, textStatus, errorThrown) {
|
||||
console.error("Status: " + textStatus);
|
||||
console.error("Error: " + errorThrown);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Function to update traffic icons based on table data
|
||||
function updateTrafficIcons(response) {
|
||||
$j('#ppp-table tbody tr').each(function(index) {
|
||||
var row = table.row(this).data();
|
||||
if (row) {
|
||||
var txValue = parseInt(row.tx, 10);
|
||||
var rxValue = parseInt(row.rx, 10);
|
||||
var iconElement = $j(this).find('.traffic-icon');
|
||||
|
||||
var maxLimit = row.max_limit;
|
||||
if (maxLimit === '1M/2M') {
|
||||
if (txValue >= 2 * 1024 * 1024 || rxValue >= 2 * 1024 * 1024) {
|
||||
iconElement.removeClass().addClass('traffic-icon traffic-icon-red');
|
||||
} else if (txValue >= 1.5 * 1024 * 1024 || rxValue >= 1.5 * 1024 * 1024) {
|
||||
iconElement.removeClass().addClass('traffic-icon traffic-icon-yellow');
|
||||
} else {
|
||||
iconElement.removeClass().addClass('traffic-icon traffic-icon-green');
|
||||
}
|
||||
} else {
|
||||
// Default logic for other max limits
|
||||
if (txValue >= 2 * 1024 * 1024 || rxValue >= 2 * 1024 * 1024) {
|
||||
iconElement.removeClass().addClass('traffic-icon traffic-icon-red');
|
||||
} else if (txValue >= 1.5 * 1024 * 1024 || rxValue >= 1.5 * 1024 * 1024) {
|
||||
iconElement.removeClass().addClass('traffic-icon traffic-icon-yellow');
|
||||
} else {
|
||||
iconElement.removeClass().addClass('traffic-icon traffic-icon-green');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Donation Popup
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
setTimeout(function() {
|
||||
document.getElementById('donationPopup').style.display = 'flex';
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
document.getElementById('donationPopup').querySelector('.modalsupport-close').addEventListener('click', function() {
|
||||
document.getElementById('donationPopup').style.display = 'none';
|
||||
});
|
||||
|
||||
window.addEventListener('click', function(event) {
|
||||
if (event.target === document.getElementById('donationPopup')) {
|
||||
document.getElementById('donationPopup').style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('donationPopup').querySelector('.donate-button').addEventListener('click', function() {
|
||||
window.open('https://buymeacoffee.com/kevindonisaputra', '_blank');
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
{include file="sections/footer.tpl"}
|
Loading…
x
Reference in New Issue
Block a user