full widget support
This commit is contained in:
parent
fca86ac4dc
commit
023b4884d1
File diff suppressed because one or more lines are too long
5
init.php
5
init.php
@ -112,6 +112,11 @@ $result = ORM::for_table('tbl_appconfig')->find_many();
|
||||
foreach ($result as $value) {
|
||||
$config[$value['setting']] = $value['value'];
|
||||
}
|
||||
|
||||
if(empty($config['dashboard_cr'])){
|
||||
$config['dashboard_cr'] = "12.7,5.12";
|
||||
}
|
||||
|
||||
$_c = $config;
|
||||
if (empty($http_proxy) && !empty($config['http_proxy'])) {
|
||||
$http_proxy = $config['http_proxy'];
|
||||
|
@ -20,6 +20,7 @@ if (isset($_GET['refresh'])) {
|
||||
r2(getUrl('dashboard'), 's', 'Data Refreshed');
|
||||
}
|
||||
|
||||
|
||||
$reset_day = $config['reset_day'];
|
||||
if (empty($reset_day)) {
|
||||
$reset_day = 1;
|
||||
@ -32,202 +33,24 @@ if (date("d") >= $reset_day) {
|
||||
}
|
||||
|
||||
$current_date = date('Y-m-d');
|
||||
$month_n = date('n');
|
||||
|
||||
$iday = ORM::for_table('tbl_transactions')
|
||||
->where('recharged_on', $current_date)
|
||||
->where_not_equal('method', 'Customer - Balance')
|
||||
->where_not_equal('method', 'Recharge Balance - Administrator')
|
||||
->sum('price');
|
||||
|
||||
if ($iday == '') {
|
||||
$iday = '0.00';
|
||||
}
|
||||
$ui->assign('iday', $iday);
|
||||
|
||||
$imonth = ORM::for_table('tbl_transactions')
|
||||
->where_not_equal('method', 'Customer - Balance')
|
||||
->where_not_equal('method', 'Recharge Balance - Administrator')
|
||||
->where_gte('recharged_on', $start_date)
|
||||
->where_lte('recharged_on', $current_date)->sum('price');
|
||||
if ($imonth == '') {
|
||||
$imonth = '0.00';
|
||||
}
|
||||
$ui->assign('imonth', $imonth);
|
||||
|
||||
if ($config['enable_balance'] == 'yes'){
|
||||
$cb = ORM::for_table('tbl_customers')->whereGte('balance', 0)->sum('balance');
|
||||
$ui->assign('cb', $cb);
|
||||
}
|
||||
|
||||
$u_act = ORM::for_table('tbl_user_recharges')->where('status', 'on')->count();
|
||||
if (empty($u_act)) {
|
||||
$u_act = '0';
|
||||
}
|
||||
$ui->assign('u_act', $u_act);
|
||||
|
||||
$u_all = ORM::for_table('tbl_user_recharges')->count();
|
||||
if (empty($u_all)) {
|
||||
$u_all = '0';
|
||||
}
|
||||
$ui->assign('u_all', $u_all);
|
||||
|
||||
|
||||
$c_all = ORM::for_table('tbl_customers')->count();
|
||||
if (empty($c_all)) {
|
||||
$c_all = '0';
|
||||
}
|
||||
$ui->assign('c_all', $c_all);
|
||||
|
||||
if ($config['hide_uet'] != 'yes') {
|
||||
//user expire
|
||||
$query = ORM::for_table('tbl_user_recharges')
|
||||
->where_lte('expiration', $current_date)
|
||||
->order_by_desc('expiration');
|
||||
$expire = Paginator::findMany($query);
|
||||
|
||||
// Get the total count of expired records for pagination
|
||||
$totalCount = ORM::for_table('tbl_user_recharges')
|
||||
->where_lte('expiration', $current_date)
|
||||
->count();
|
||||
|
||||
// Pass the total count and current page to the paginator
|
||||
$paginator['total_count'] = $totalCount;
|
||||
|
||||
// Assign the pagination HTML to the template variable
|
||||
$ui->assign('expire', $expire);
|
||||
}
|
||||
|
||||
//activity log
|
||||
$dlog = ORM::for_table('tbl_logs')->limit(5)->order_by_desc('id')->find_many();
|
||||
$ui->assign('dlog', $dlog);
|
||||
$log = ORM::for_table('tbl_logs')->count();
|
||||
$ui->assign('log', $log);
|
||||
|
||||
|
||||
if ($config['hide_vs'] != 'yes') {
|
||||
$cacheStocksfile = $CACHE_PATH . File::pathFixer('/VoucherStocks.temp');
|
||||
$cachePlanfile = $CACHE_PATH . File::pathFixer('/VoucherPlans.temp');
|
||||
//Cache for 5 minutes
|
||||
if (file_exists($cacheStocksfile) && time() - filemtime($cacheStocksfile) < 600) {
|
||||
$stocks = json_decode(file_get_contents($cacheStocksfile), true);
|
||||
$plans = json_decode(file_get_contents($cachePlanfile), true);
|
||||
} else {
|
||||
// Count stock
|
||||
$tmp = $v = ORM::for_table('tbl_plans')->select('id')->select('name_plan')->find_many();
|
||||
$plans = array();
|
||||
$stocks = array("used" => 0, "unused" => 0);
|
||||
$n = 0;
|
||||
foreach ($tmp as $plan) {
|
||||
$unused = ORM::for_table('tbl_voucher')
|
||||
->where('id_plan', $plan['id'])
|
||||
->where('status', 0)->count();
|
||||
$used = ORM::for_table('tbl_voucher')
|
||||
->where('id_plan', $plan['id'])
|
||||
->where('status', 1)->count();
|
||||
if ($unused > 0 || $used > 0) {
|
||||
$plans[$n]['name_plan'] = $plan['name_plan'];
|
||||
$plans[$n]['unused'] = $unused;
|
||||
$plans[$n]['used'] = $used;
|
||||
$stocks["unused"] += $unused;
|
||||
$stocks["used"] += $used;
|
||||
$n++;
|
||||
}
|
||||
}
|
||||
file_put_contents($cacheStocksfile, json_encode($stocks));
|
||||
file_put_contents($cachePlanfile, json_encode($plans));
|
||||
}
|
||||
}
|
||||
|
||||
$cacheMRfile = File::pathFixer('/monthlyRegistered.temp');
|
||||
//Cache for 1 hour
|
||||
if (file_exists($cacheMRfile) && time() - filemtime($cacheMRfile) < 3600) {
|
||||
$monthlyRegistered = json_decode(file_get_contents($cacheMRfile), true);
|
||||
} else {
|
||||
//Monthly Registered Customers
|
||||
$result = ORM::for_table('tbl_customers')
|
||||
->select_expr('MONTH(created_at)', 'month')
|
||||
->select_expr('COUNT(*)', 'count')
|
||||
->where_raw('YEAR(created_at) = YEAR(NOW())')
|
||||
->group_by_expr('MONTH(created_at)')
|
||||
->find_many();
|
||||
|
||||
$monthlyRegistered = [];
|
||||
foreach ($result as $row) {
|
||||
$monthlyRegistered[] = [
|
||||
'date' => $row->month,
|
||||
'count' => $row->count
|
||||
];
|
||||
}
|
||||
file_put_contents($cacheMRfile, json_encode($monthlyRegistered));
|
||||
}
|
||||
|
||||
$cacheMSfile = $CACHE_PATH . File::pathFixer('/monthlySales.temp');
|
||||
//Cache for 12 hours
|
||||
if (file_exists($cacheMSfile) && time() - filemtime($cacheMSfile) < 43200) {
|
||||
$monthlySales = json_decode(file_get_contents($cacheMSfile), true);
|
||||
} else {
|
||||
// Query to retrieve monthly data
|
||||
$results = ORM::for_table('tbl_transactions')
|
||||
->select_expr('MONTH(recharged_on)', 'month')
|
||||
->select_expr('SUM(price)', 'total')
|
||||
->where_raw("YEAR(recharged_on) = YEAR(CURRENT_DATE())") // Filter by the current year
|
||||
->where_not_equal('method', 'Customer - Balance')
|
||||
->where_not_equal('method', 'Recharge Balance - Administrator')
|
||||
->group_by_expr('MONTH(recharged_on)')
|
||||
->find_many();
|
||||
|
||||
// Create an array to hold the monthly sales data
|
||||
$monthlySales = array();
|
||||
|
||||
// Iterate over the results and populate the array
|
||||
foreach ($results as $result) {
|
||||
$month = $result->month;
|
||||
$totalSales = $result->total;
|
||||
|
||||
$monthlySales[$month] = array(
|
||||
'month' => $month,
|
||||
'totalSales' => $totalSales
|
||||
);
|
||||
}
|
||||
|
||||
// Fill in missing months with zero sales
|
||||
for ($month = 1; $month <= 12; $month++) {
|
||||
if (!isset($monthlySales[$month])) {
|
||||
$monthlySales[$month] = array(
|
||||
'month' => $month,
|
||||
'totalSales' => 0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the array by month
|
||||
ksort($monthlySales);
|
||||
|
||||
// Reindex the array
|
||||
$monthlySales = array_values($monthlySales);
|
||||
file_put_contents($cacheMSfile, json_encode($monthlySales));
|
||||
}
|
||||
|
||||
if ($config['router_check']) {
|
||||
$routeroffs = ORM::for_table('tbl_routers')->selects(['id', 'name', 'last_seen'])->where('status', 'Offline')->where('enabled', '1')->order_by_desc('name')->find_array();
|
||||
$ui->assign('routeroffs', $routeroffs);
|
||||
}
|
||||
|
||||
$timestampFile = "$UPLOAD_PATH/cron_last_run.txt";
|
||||
if (file_exists($timestampFile)) {
|
||||
$lastRunTime = file_get_contents($timestampFile);
|
||||
$ui->assign('run_date', date('Y-m-d h:i:s A', $lastRunTime));
|
||||
}
|
||||
|
||||
// Assign the monthly sales data to Smarty
|
||||
$ui->assign('start_date', $start_date);
|
||||
$ui->assign('current_date', $current_date);
|
||||
$ui->assign('monthlySales', $monthlySales);
|
||||
$ui->assign('xfooter', '');
|
||||
$ui->assign('monthlyRegistered', $monthlyRegistered);
|
||||
$ui->assign('stocks', $stocks);
|
||||
$ui->assign('plans', $plans);
|
||||
|
||||
$widgets = ORM::for_table('tbl_widgets')->selects("enabled", 1)->order_by_asc("orders")->findArray();
|
||||
$count = count($widgets);
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
try{
|
||||
if(file_exists($WIDGET_PATH . DIRECTORY_SEPARATOR . $widgets[$i]['widget'].".php")){
|
||||
require_once $WIDGET_PATH . DIRECTORY_SEPARATOR . $widgets[$i]['widget'].".php";
|
||||
$widgets[$i]['content'] = (new $widgets[$i]['widget'])->getWidget($widgets[$i]);
|
||||
}else{
|
||||
$widgets[$i]['content'] = "Widget not found";
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
$widgets[$i]['content'] = $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
$ui->assign('widgets', $widgets);
|
||||
run_hook('view_dashboard'); #HOOK
|
||||
$ui->display('admin/dashboard.tpl');
|
@ -1,56 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', Lang::T('Dashboard'));
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
if (isset($_GET['refresh'])) {
|
||||
$files = scandir($CACHE_PATH);
|
||||
foreach ($files as $file) {
|
||||
$ext = pathinfo($file, PATHINFO_EXTENSION);
|
||||
if (is_file($CACHE_PATH . DIRECTORY_SEPARATOR . $file) && $ext == 'temp') {
|
||||
unlink($CACHE_PATH . DIRECTORY_SEPARATOR . $file);
|
||||
}
|
||||
}
|
||||
r2(getUrl('dashboard'), 's', 'Data Refreshed');
|
||||
}
|
||||
|
||||
|
||||
$reset_day = $config['reset_day'];
|
||||
if (empty($reset_day)) {
|
||||
$reset_day = 1;
|
||||
}
|
||||
//first day of month
|
||||
if (date("d") >= $reset_day) {
|
||||
$start_date = date('Y-m-' . $reset_day);
|
||||
} else {
|
||||
$start_date = date('Y-m-' . $reset_day, strtotime("-1 MONTH"));
|
||||
}
|
||||
|
||||
$current_date = date('Y-m-d');
|
||||
$ui->assign('start_date', $start_date);
|
||||
$ui->assign('current_date', $current_date);
|
||||
|
||||
$widgets = ORM::for_table('tbl_widgets')->selects("enabled", 1)->order_by_asc("orders")->findArray();
|
||||
$count = count($widgets);
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
try{
|
||||
if(file_exists($WIDGET_PATH . DIRECTORY_SEPARATOR . $widgets[$i]['widget'].".php")){
|
||||
require_once $WIDGET_PATH . DIRECTORY_SEPARATOR . $widgets[$i]['widget'].".php";
|
||||
$widgets[$i]['content'] = (new $widgets[$i]['widget'])->getWidget($widgets[$i]);
|
||||
}else{
|
||||
$widgets[$i]['content'] = "Widget not found";
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
$widgets[$i]['content'] = $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
$ui->assign('widgets', $widgets);
|
||||
run_hook('view_dashboard'); #HOOK
|
||||
$ui->display('admin/dashboard_widget.tpl');
|
@ -11,6 +11,13 @@ $ui->assign('_system_menu', 'settings');
|
||||
$action = alphanumeric($routes['1']);
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
$max = ORM::for_table('tbl_widgets')->max('position');
|
||||
$max2 = substr_count($config['dashboard_cr'], '.')+substr_count($config['dashboard_cr'], ',')+1;
|
||||
if($max2>$max){
|
||||
$max = $max2;
|
||||
}
|
||||
$ui->assign('max', $max);
|
||||
|
||||
if ($action == 'add') {
|
||||
$pos = alphanumeric($routes['2']);
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
@ -104,9 +111,20 @@ if ($action == 'add') {
|
||||
}
|
||||
r2(getUrl('widgets'), 's', 'Widget order Saved Successfully');
|
||||
} else {
|
||||
if(_post("save") == 'struct'){
|
||||
$d = ORM::for_table('tbl_appconfig')->where('setting', 'dashboard_cr')->find_one();
|
||||
if ($d) {
|
||||
$d->value = _post('dashboard_cr');
|
||||
$d->save();
|
||||
} else {
|
||||
$d = ORM::for_table('tbl_appconfig')->create();
|
||||
$d->setting = 'dashboard_cr';
|
||||
$d->value = _post('dashboard_cr');
|
||||
$d->save();
|
||||
}
|
||||
_alert("Dashboard Structure Saved Successfully", "success", getUrl('widgets'));
|
||||
}
|
||||
$widgets = ORM::for_table('tbl_widgets')->selects("position", 1)->order_by_asc("orders")->find_many();
|
||||
$max = ORM::for_table('tbl_widgets')->max('position');
|
||||
$ui->assign('widgets', $widgets);
|
||||
$ui->assign('max', $max);
|
||||
$ui->display('admin/settings/widgets.tpl');
|
||||
}
|
||||
|
@ -1049,5 +1049,8 @@
|
||||
"Sending___": "Sending...",
|
||||
"Message_sent_successfully_": "Message sent successfully.",
|
||||
"Error_sending_message__": "Error sending message: ",
|
||||
"Failed_to_send_the_message__Please_try_again_": "Failed to send the message. Please try again."
|
||||
"Failed_to_send_the_message__Please_try_again_": "Failed to send the message. Please try again.",
|
||||
"Dashboard_Structure": "Dashboard Structure",
|
||||
"Read_documentation": "Read documentation",
|
||||
"Structure": "Structure"
|
||||
}
|
@ -1,420 +1,37 @@
|
||||
{include file="sections/header.tpl"}
|
||||
|
||||
<div class="row">
|
||||
{if in_array($_admin['user_type'],['SuperAdmin','Admin', 'Report'])}
|
||||
<div class="col-lg-3 col-xs-6">
|
||||
<div class="small-box bg-aqua">
|
||||
<div class="inner">
|
||||
<h4 class="text-bold" style="font-size: large;"><sup>{$_c['currency_code']}</sup>
|
||||
{number_format($iday,0,$_c['dec_point'],$_c['thousands_sep'])}</h4>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="ion ion-clock"></i>
|
||||
</div>
|
||||
<a href="{Text::url('reports/by-date')}" class="small-box-footer">{Lang::T('Income Today')}</a>
|
||||
{function showWidget pos=0}
|
||||
{foreach $widgets as $w}
|
||||
{if $w['position'] == $pos}
|
||||
{$w['content']}
|
||||
{/if}
|
||||
{/foreach}
|
||||
{/function}
|
||||
|
||||
|
||||
{assign rows explode(".", $_c['dashboard_cr'])}
|
||||
{assign pos 1}
|
||||
{foreach $rows as $cols}
|
||||
{if $cols == 12}
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
{showWidget widgets=$widgets pos=$pos}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-3 col-xs-6">
|
||||
<div class="small-box bg-green">
|
||||
<div class="inner">
|
||||
<h4 class="text-bold" style="font-size: large;"><sup>{$_c['currency_code']}</sup>
|
||||
{number_format($imonth,0,$_c['dec_point'],$_c['thousands_sep'])}</h4>
|
||||
{assign pos value=$pos+1}
|
||||
{else}
|
||||
{assign colss explode(",", $cols)}
|
||||
<div class="row">
|
||||
{foreach $colss as $c}
|
||||
<div class="col-md-{$c}">
|
||||
{showWidget widgets=$widgets pos=$pos}
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="ion ion-android-calendar"></i>
|
||||
</div>
|
||||
<a href="{Text::url('reports/by-period')}" class="small-box-footer">{Lang::T('Income This Month')}</a>
|
||||
</div>
|
||||
{assign pos value=$pos+1}
|
||||
{/foreach}
|
||||
</div>
|
||||
{/if}
|
||||
<div class="col-lg-3 col-xs-6">
|
||||
<div class="small-box bg-yellow">
|
||||
<div class="inner">
|
||||
<h4 class="text-bold" style="font-size: large;">{$u_act}/{$u_all-$u_act}</h4>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="ion ion-person"></i>
|
||||
</div>
|
||||
<a href="{Text::url('plan/list')}" class="small-box-footer">{Lang::T('Active')}/{Lang::T('Expired')}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-3 col-xs-6">
|
||||
<div class="small-box bg-red">
|
||||
<div class="inner">
|
||||
<h4 class="text-bold" style="font-size: large;">{$c_all}</h4>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="ion ion-android-people"></i>
|
||||
</div>
|
||||
<a href="{Text::url('customers/list')}" class="small-box-footer">{Lang::T('Customers')}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ol class="breadcrumb">
|
||||
<li>{Lang::dateFormat($start_date)}</li>
|
||||
<li>{Lang::dateFormat($current_date)}</li>
|
||||
{if $_c['enable_balance'] == 'yes' && in_array($_admin['user_type'],['SuperAdmin','Admin', 'Report'])}
|
||||
<li onclick="window.location.href = '{Text::url('customers&search=&order=balance&filter=Active&orderby=desc')}'" style="cursor: pointer;">
|
||||
{Lang::T('Customer Balance')} <sup>{$_c['currency_code']}</sup>
|
||||
<b>{number_format($cb,0,$_c['dec_point'],$_c['thousands_sep'])}</b>
|
||||
</li>
|
||||
{/if}
|
||||
</ol>
|
||||
<div class="row">
|
||||
<div class="col-md-7">
|
||||
{/foreach}
|
||||
|
||||
<!-- solid sales graph -->
|
||||
{if $_c['hide_mrc'] != 'yes'}
|
||||
<div class="box box-solid ">
|
||||
<div class="box-header">
|
||||
<i class="fa fa-th"></i>
|
||||
|
||||
<h3 class="box-title">{Lang::T('Monthly Registered Customers')}</h3>
|
||||
|
||||
<div class="box-tools pull-right">
|
||||
<button type="button" class="btn bg-teal btn-sm" data-widget="collapse"><i class="fa fa-minus"></i>
|
||||
</button>
|
||||
<a href="{Text::url('dashboard&refresh')}" class="btn bg-teal btn-sm"><i class="fa fa-refresh"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-body border-radius-none">
|
||||
<canvas class="chart" id="chart" style="height: 250px;"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- solid sales graph -->
|
||||
{if $_c['hide_tms'] != 'yes'}
|
||||
<div class="box box-solid ">
|
||||
<div class="box-header">
|
||||
<i class="fa fa-inbox"></i>
|
||||
|
||||
<h3 class="box-title">{Lang::T('Total Monthly Sales')}</h3>
|
||||
|
||||
<div class="box-tools pull-right">
|
||||
<button type="button" class="btn bg-teal btn-sm" data-widget="collapse"><i class="fa fa-minus"></i>
|
||||
</button>
|
||||
<a href="{Text::url('dashboard&refresh')}" class="btn bg-teal btn-sm"><i class="fa fa-refresh"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-body border-radius-none">
|
||||
<canvas class="chart" id="salesChart" style="height: 250px;"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{if $_c['disable_voucher'] != 'yes' && $stocks['unused']>0 || $stocks['used']>0}
|
||||
{if $_c['hide_vs'] != 'yes'}
|
||||
<div class="panel panel-primary mb20 panel-hovered project-stats table-responsive">
|
||||
<div class="panel-heading">Vouchers Stock</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{Lang::T('Package Name')}</th>
|
||||
<th>unused</th>
|
||||
<th>used</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach $plans as $stok}
|
||||
<tr>
|
||||
<td>{$stok['name_plan']}</td>
|
||||
<td>{$stok['unused']}</td>
|
||||
<td>{$stok['used']}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
{/foreach}
|
||||
<tr>
|
||||
<td>Total</td>
|
||||
<td>{$stocks['unused']}</td>
|
||||
<td>{$stocks['used']}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
{if $_c['hide_uet'] != 'yes'}
|
||||
<div class="panel panel-warning mb20 panel-hovered project-stats table-responsive">
|
||||
<div class="panel-heading">{Lang::T('User Expired, Today')}</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{Lang::T('Username')}</th>
|
||||
<th>{Lang::T('Created / Expired')}</th>
|
||||
<th>{Lang::T('Internet Package')}</th>
|
||||
<th>{Lang::T('Location')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach $expire as $expired}
|
||||
{assign var="rem_exp" value="{$expired['expiration']} {$expired['time']}"}
|
||||
{assign var="rem_started" value="{$expired['recharged_on']} {$expired['recharged_time']}"}
|
||||
<tr>
|
||||
<td><a href="{Text::url('customers/viewu/',$expired['username'])}">{$expired['username']}</a></td>
|
||||
<td><small data-toggle="tooltip" data-placement="top"
|
||||
title="{Lang::dateAndTimeFormat($expired['recharged_on'],$expired['recharged_time'])}">{Lang::timeElapsed($rem_started)}</small>
|
||||
/
|
||||
<span data-toggle="tooltip" data-placement="top"
|
||||
title="{Lang::dateAndTimeFormat($expired['expiration'],$expired['time'])}">{Lang::timeElapsed($rem_exp)}</span>
|
||||
</td>
|
||||
<td>{$expired['namebp']}</td>
|
||||
<td>{$expired['routers']}</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{include file="pagination.tpl"}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-md-5">
|
||||
{if $_c['router_check'] && count($routeroffs)> 0}
|
||||
<div class="panel panel-danger">
|
||||
<div class="panel-heading text-bold">{Lang::T('Routers Offline')}</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-condensed">
|
||||
<tbody>
|
||||
{foreach $routeroffs as $ros}
|
||||
<tr>
|
||||
<td><a href="{Text::url('routers/edit/',$ros['id'])}" class="text-bold text-red">{$ros['name']}</a></td>
|
||||
<td data-toggle="tooltip" data-placement="top" class="text-red"
|
||||
title="{Lang::dateTimeFormat($ros['last_seen'])}">{Lang::timeElapsed($ros['last_seen'])}
|
||||
</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{if $run_date}
|
||||
{assign var="current_time" value=$smarty.now}
|
||||
{assign var="run_time" value=strtotime($run_date)}
|
||||
{if $current_time - $run_time > 3600}
|
||||
<div class="panel panel-cron-warning panel-hovered mb20 activities">
|
||||
<div class="panel-heading"><i class="fa fa-clock-o"></i> {Lang::T('Cron has not run for over 1 hour. Please
|
||||
check your setup.')}</div>
|
||||
</div>
|
||||
{else}
|
||||
<div class="panel panel-cron-success panel-hovered mb20 activities">
|
||||
<div class="panel-heading">{Lang::T('Cron Job last ran on')}: {$run_date}</div>
|
||||
</div>
|
||||
{/if}
|
||||
{else}
|
||||
<div class="panel panel-cron-danger panel-hovered mb20 activities">
|
||||
<div class="panel-heading"><i class="fa fa-warning"></i> {Lang::T('Cron appear not been setup, please check
|
||||
your cron setup.')}</div>
|
||||
</div>
|
||||
{/if}
|
||||
{if $_c['hide_pg'] != 'yes'}
|
||||
<div class="panel panel-success panel-hovered mb20 activities">
|
||||
<div class="panel-heading">{Lang::T('Payment Gateway')}: {str_replace(',',', ',$_c['payment_gateway'])}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{if $_c['hide_aui'] != 'yes'}
|
||||
<div class="panel panel-info panel-hovered mb20 activities">
|
||||
<div class="panel-heading">{Lang::T('All Users Insights')}</div>
|
||||
<div class="panel-body">
|
||||
<canvas id="userRechargesChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{if $_c['hide_al'] != 'yes'}
|
||||
<div class="panel panel-info panel-hovered mb20 activities">
|
||||
<div class="panel-heading"><a href="{Text::url('logs')}">{Lang::T('Activity Log')}</a></div>
|
||||
<div class="panel-body">
|
||||
<ul class="list-unstyled">
|
||||
{foreach $dlog as $dlogs}
|
||||
<li class="primary">
|
||||
<span class="point"></span>
|
||||
<span class="time small text-muted">{Lang::timeElapsed($dlogs['date'],true)}</span>
|
||||
<p>{$dlogs['description']}</p>
|
||||
</li>
|
||||
{/foreach}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.5.1/dist/chart.min.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
{if $_c['hide_mrc'] != 'yes'}
|
||||
{literal}
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
var counts = JSON.parse('{/literal}{$monthlyRegistered|json_encode}{literal}');
|
||||
|
||||
var monthNames = [
|
||||
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
|
||||
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
|
||||
];
|
||||
|
||||
var labels = [];
|
||||
var data = [];
|
||||
|
||||
for (var i = 1; i <= 12; i++) {
|
||||
var month = counts.find(count => count.date === i);
|
||||
labels.push(month ? monthNames[i - 1] : monthNames[i - 1].substring(0, 3));
|
||||
data.push(month ? month.count : 0);
|
||||
}
|
||||
|
||||
var ctx = document.getElementById('chart').getContext('2d');
|
||||
var chart = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: [{
|
||||
label: 'Registered Members',
|
||||
data: data,
|
||||
backgroundColor: 'rgba(0, 0, 255, 0.5)',
|
||||
borderColor: 'rgba(0, 0, 255, 0.7)',
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
scales: {
|
||||
x: {
|
||||
grid: {
|
||||
display: false
|
||||
}
|
||||
},
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
grid: {
|
||||
color: 'rgba(0, 0, 0, 0.1)'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
{/literal}
|
||||
{/if}
|
||||
{if $_c['hide_tmc'] != 'yes'}
|
||||
{literal}
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
var monthlySales = JSON.parse('{/literal}{$monthlySales|json_encode}{literal}');
|
||||
|
||||
var monthNames = [
|
||||
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
|
||||
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
|
||||
];
|
||||
|
||||
var labels = [];
|
||||
var data = [];
|
||||
|
||||
for (var i = 1; i <= 12; i++) {
|
||||
var month = findMonthData(monthlySales, i);
|
||||
labels.push(month ? monthNames[i - 1] : monthNames[i - 1].substring(0, 3));
|
||||
data.push(month ? month.totalSales : 0);
|
||||
}
|
||||
|
||||
var ctx = document.getElementById('salesChart').getContext('2d');
|
||||
var chart = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: [{
|
||||
label: 'Monthly Sales',
|
||||
data: data,
|
||||
backgroundColor: 'rgba(2, 10, 242)', // Customize the background color
|
||||
borderColor: 'rgba(255, 99, 132, 1)', // Customize the border color
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
scales: {
|
||||
x: {
|
||||
grid: {
|
||||
display: false
|
||||
}
|
||||
},
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
grid: {
|
||||
color: 'rgba(0, 0, 0, 0.1)'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function findMonthData(monthlySales, month) {
|
||||
for (var i = 0; i < monthlySales.length; i++) {
|
||||
if (monthlySales[i].month === month) {
|
||||
return monthlySales[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
{/literal}
|
||||
{/if}
|
||||
{if $_c['hide_aui'] != 'yes'}
|
||||
{literal}
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
// Get the data from PHP and assign it to JavaScript variables
|
||||
var u_act = '{/literal}{$u_act}{literal}';
|
||||
var c_all = '{/literal}{$c_all}{literal}';
|
||||
var u_all = '{/literal}{$u_all}{literal}';
|
||||
//lets calculate the inactive users as reported
|
||||
var expired = u_all - u_act;
|
||||
var inactive = c_all - u_all;
|
||||
if (inactive < 0) {
|
||||
inactive = 0;
|
||||
}
|
||||
// Create the chart data
|
||||
var data = {
|
||||
labels: ['Active Users', 'Expired Users', 'Inactive Users'],
|
||||
datasets: [{
|
||||
label: 'User Recharges',
|
||||
data: [parseInt(u_act), parseInt(expired), parseInt(inactive)],
|
||||
backgroundColor: ['rgba(4, 191, 13)', 'rgba(191, 35, 4)', 'rgba(0, 0, 255, 0.5'],
|
||||
borderColor: ['rgba(0, 255, 0, 1)', 'rgba(255, 99, 132, 1)', 'rgba(0, 0, 255, 0.7'],
|
||||
borderWidth: 1
|
||||
}]
|
||||
};
|
||||
|
||||
// Create chart options
|
||||
var options = {
|
||||
responsive: true,
|
||||
aspectRatio: 1,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'bottom',
|
||||
labels: {
|
||||
boxWidth: 15
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Get the canvas element and create the chart
|
||||
var ctx = document.getElementById('userRechargesChart').getContext('2d');
|
||||
var chart = new Chart(ctx, {
|
||||
type: 'pie',
|
||||
data: data,
|
||||
options: options
|
||||
});
|
||||
});
|
||||
{/literal}
|
||||
{/if}
|
||||
</script>
|
||||
{if $_c['new_version_notify'] != 'disable'}
|
||||
<script>
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
|
@ -1,94 +0,0 @@
|
||||
{include file="sections/header.tpl"}
|
||||
|
||||
{function showWidget pos=0}
|
||||
{foreach $widgets as $w}
|
||||
{if $w['position'] == $pos}
|
||||
{$w['content']}
|
||||
{/if}
|
||||
{/foreach}
|
||||
{/function}
|
||||
|
||||
|
||||
|
||||
{if $_c['dashboard'] == '1.55.1'}
|
||||
{showWidget widgets=$widgets pos=1}
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{showWidget widgets=$widgets pos=2}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{showWidget widgets=$widgets pos=3}
|
||||
</div>
|
||||
</div>
|
||||
{showWidget widgets=$widgets pos=4}
|
||||
{elseif $_c['dashboard'] == '1.57.1'}
|
||||
{showWidget widgets=$widgets pos=1}
|
||||
<div class="row">
|
||||
<div class="col-md-5">
|
||||
{showWidget widgets=$widgets pos=2}
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
{showWidget widgets=$widgets pos=3}
|
||||
</div>
|
||||
</div>
|
||||
{showWidget widgets=$widgets pos=4}
|
||||
{elseif $_c['dashboard'] == '1.1.1.1'}
|
||||
{showWidget widgets=$widgets pos=1}
|
||||
{showWidget widgets=$widgets pos=2}
|
||||
{showWidget widgets=$widgets pos=3}
|
||||
{showWidget widgets=$widgets pos=4}
|
||||
{else}
|
||||
{showWidget widgets=$widgets pos=1}
|
||||
<div class="row">
|
||||
<div class="col-md-7">
|
||||
{showWidget widgets=$widgets pos=2}
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
{showWidget widgets=$widgets pos=3}
|
||||
</div>
|
||||
</div>
|
||||
{showWidget widgets=$widgets pos=4}
|
||||
{/if}
|
||||
|
||||
{if $_c['new_version_notify'] != 'disable'}
|
||||
<script>
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
$.getJSON("./version.json?" + Math.random(), function(data) {
|
||||
var localVersion = data.version;
|
||||
$('#version').html('Version: ' + localVersion);
|
||||
$.getJSON(
|
||||
"https://raw.githubusercontent.com/hotspotbilling/phpnuxbill/master/version.json?" +
|
||||
Math
|
||||
.random(),
|
||||
function(data) {
|
||||
var latestVersion = data.version;
|
||||
if (localVersion !== latestVersion) {
|
||||
$('#version').html('Latest Version: ' + latestVersion);
|
||||
if (getCookie(latestVersion) != 'done') {
|
||||
Swal.fire({
|
||||
icon: 'info',
|
||||
title: "New Version Available\nVersion: " + latestVersion,
|
||||
toast: true,
|
||||
position: 'bottom-right',
|
||||
showConfirmButton: true,
|
||||
showCloseButton: true,
|
||||
timer: 30000,
|
||||
confirmButtonText: '<a href="{Text::url('community')}#latestVersion" style="color: white;">Update Now</a>',
|
||||
timerProgressBar: true,
|
||||
didOpen: (toast) => {
|
||||
toast.addEventListener('mouseenter', Swal.stopTimer)
|
||||
toast.addEventListener('mouseleave', Swal
|
||||
.resumeTimer)
|
||||
}
|
||||
});
|
||||
setCookie(latestVersion, 'done', 7);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
{/if}
|
||||
|
||||
{include file="sections/footer.tpl"}
|
@ -1,6 +1,6 @@
|
||||
{include file="sections/header.tpl"}
|
||||
|
||||
<form class="form-horizontal" method="post" role="form" action="{Text::url('')}settings/notifications-post">
|
||||
<form class="form-horizontal" method="post" role="form" action="{Text::url('settings/notifications-post')}">
|
||||
<input type="hidden" name="csrf_token" value="{$csrf_token}">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
@ -26,7 +26,7 @@
|
||||
<b>[[package]]</b> - {Lang::T('will be replaced with Package name')}.<br>
|
||||
<b>[[price]]</b> - {Lang::T('will be replaced with Package price')}.<br>
|
||||
<b>[[bills]]</b> - {Lang::T('additional bills for customers')}.<br>
|
||||
<b>[[payment_link]]</b> - <a href="{Text::url('docs')}/docs/#Reminder%20with%20payment%20link"
|
||||
<b>[[payment_link]]</b> - <a href="{$app_url}/docs/#Reminder%20with%20payment%20link"
|
||||
target="_blank">read documentation</a>.
|
||||
</p>
|
||||
</div>
|
||||
@ -45,7 +45,7 @@
|
||||
<b>[[price]]</b> - {Lang::T('will be replaced with Package price')}.<br>
|
||||
<b>[[expired_date]]</b> - {Lang::T('will be replaced with Expiration date')}.<br>
|
||||
<b>[[bills]]</b> - {Lang::T('additional bills for customers')}.<br>
|
||||
<b>[[payment_link]]</b> - <a href="{Text::url('docs')}/docs/#Reminder%20with%20payment%20link"
|
||||
<b>[[payment_link]]</b> - <a href="{$app_url}/docs/#Reminder%20with%20payment%20link"
|
||||
target="_blank">read documentation</a>.
|
||||
</p>
|
||||
</div>
|
||||
@ -64,7 +64,7 @@
|
||||
<b>[[price]]</b> - {Lang::T('will be replaced with Package price')}.<br>
|
||||
<b>[[expired_date]]</b> - {Lang::T('will be replaced with Expiration date')}.<br>
|
||||
<b>[[bills]]</b> - {Lang::T('additional bills for customers')}.<br>
|
||||
<b>[[payment_link]]</b> - <a href="{Text::url('docs')}/docs/#Reminder%20with%20payment%20link"
|
||||
<b>[[payment_link]]</b> - <a href="{$app_url}/docs/#Reminder%20with%20payment%20link"
|
||||
target="_blank">read documentation</a>.
|
||||
</p>
|
||||
</div>
|
||||
@ -83,7 +83,7 @@
|
||||
<b>[[price]]</b> - {Lang::T('will be replaced with Package price')}.<br>
|
||||
<b>[[expired_date]]</b> - {Lang::T('will be replaced with Expiration date')}.<br>
|
||||
<b>[[bills]]</b> - {Lang::T('additional bills for customers')}.<br>
|
||||
<b>[[payment_link]]</b> - <a href="{Text::url('docs')}/docs/#Reminder%20with%20payment%20link"
|
||||
<b>[[payment_link]]</b> - <a href="{$app_url}/docs/#Reminder%20with%20payment%20link"
|
||||
target="_blank">read documentation</a>.
|
||||
</p>
|
||||
</div>
|
||||
|
@ -1,5 +1,8 @@
|
||||
{include file="sections/header.tpl"}
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
{function showWidget pos=0}
|
||||
<form method="post" action="{Text::url('widgets/pos/')}">
|
||||
<div class="panel panel-info">
|
||||
@ -49,11 +52,64 @@
|
||||
{/function}
|
||||
|
||||
<div class="row">
|
||||
{for $pos=1 to $max}
|
||||
<div class="col-md-6 col-lg-4">
|
||||
{showWidget widgets=$widgets pos=$pos}
|
||||
<div class="col-md-3">
|
||||
<div class="panel panel-info">
|
||||
<div class="panel-heading">Dashboard Structure</div>
|
||||
<div class="panel-body">
|
||||
{assign rows explode(".", $_c['dashboard_cr'])}
|
||||
{assign pos 1}
|
||||
{foreach $rows as $cols}
|
||||
{if $cols == 12}
|
||||
<div class="row row-no-gutters">
|
||||
<div class="col-xs-12" style="border: 1px;">
|
||||
<a href="{Text::url('widgets/add/', $pos)}" class="btn btn-default btn-block">{$pos}</a>
|
||||
</div>
|
||||
</div>
|
||||
{assign pos value=$pos+1}
|
||||
{else}
|
||||
{assign colss explode(",", $cols)}
|
||||
<div class="row row-no-gutters">
|
||||
{foreach $colss as $c}
|
||||
<div class="col-xs-{$c}">
|
||||
<a href="{Text::url('widgets/add/', $pos)}" class="btn btn-default btn-block">{$pos}</a>
|
||||
</div>
|
||||
{assign pos value=$pos+1}
|
||||
{/foreach}
|
||||
</div>
|
||||
{/if}
|
||||
{/foreach}
|
||||
</div>
|
||||
<div class="panel-footer">
|
||||
<form method="post" action="{Text::url('widgets')}">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><a href="{$app_url}/docs/#Dashboard%20Structure"
|
||||
target="_blank">{Lang::T("Structure")}</a></span>
|
||||
<input type="text" name="dashboard_cr" value="{$_c['dashboard_cr']}" class="form-control"
|
||||
placeholder="Dashboard">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary btn-block" name="save" value="struct">{Lang::T("Save")}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{/for}
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{for $pos=1 to $max}
|
||||
{if $pos%2 != 0}
|
||||
{showWidget widgets=$widgets pos=$pos}
|
||||
{/if}
|
||||
{/for}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{for $pos=1 to $max}
|
||||
{if $pos%2 == 0}
|
||||
{showWidget widgets=$widgets pos=$pos}
|
||||
{/if}
|
||||
{/for}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{include file="sections/footer.tpl"}
|
@ -40,7 +40,7 @@
|
||||
<label class="col-md-3 control-label">{Lang::T('Position')}</label>
|
||||
<div class="col-md-5">
|
||||
<select name="position" id="position" class="form-control">
|
||||
{for $pos=1 to 12}
|
||||
{for $pos=1 to $max}
|
||||
<option value="{$pos}" {if $widget['position'] eq $pos}selected="selected" {/if}>
|
||||
Area {$pos}
|
||||
</option>
|
||||
|
@ -1,3 +1,3 @@
|
||||
{
|
||||
"version": "2025.2.12.1"
|
||||
"version": "2025.2.18"
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user