getting ready for customizeable dashboard with widget
This commit is contained in:
parent
30bdb89d91
commit
eff0c7dab7
1
init.php
1
init.php
@ -56,6 +56,7 @@ $UPLOAD_PATH = $root_path . File::pathFixer('system/uploads');
|
||||
$CACHE_PATH = $root_path . File::pathFixer('system/cache');
|
||||
$PAGES_PATH = $root_path . File::pathFixer('pages');
|
||||
$PLUGIN_PATH = $root_path . File::pathFixer('system/plugin');
|
||||
$WIDGET_PATH = $root_path . File::pathFixer('system/widgets');
|
||||
$PAYMENTGATEWAY_PATH = $root_path . File::pathFixer('system/paymentgateway');
|
||||
$UI_PATH = 'ui';
|
||||
|
||||
|
@ -19,6 +19,7 @@ class Paginator
|
||||
$url .= '&' . http_build_query($search);
|
||||
}
|
||||
$url .= $append_url.'&p=';
|
||||
$url = Text::fixUrl($url);
|
||||
$totalReq = $query->count();
|
||||
$lastpage = ceil($totalReq / $per_page);
|
||||
$lpm1 = $lastpage - 1;
|
||||
|
@ -132,6 +132,14 @@ class Text
|
||||
}
|
||||
}
|
||||
|
||||
public static function fixUrl($url){
|
||||
//if url dont have ? then add it with replace first & to ?
|
||||
if(strpos($url, '?') === false && strpos($url, '&')!== false){
|
||||
return substr($url, 0, strpos($url, '&')). '?'. substr($url, strpos($url, '&')+1);
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
// this will return & or ?
|
||||
public static function isQA(){
|
||||
global $config;
|
||||
|
50
system/autoload/Widget.php
Normal file
50
system/autoload/Widget.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
|
||||
/**
|
||||
* Validator class
|
||||
*/
|
||||
class Widget
|
||||
{
|
||||
|
||||
public static function rows($rows, $result){
|
||||
$result .= '<div class="row">';
|
||||
foreach($rows as $row){
|
||||
|
||||
}
|
||||
$result .= '</div>';
|
||||
}
|
||||
|
||||
public static function columns($cols, $result){
|
||||
$c = count($cols);
|
||||
switch($c){
|
||||
case 1:
|
||||
$result .= '<div class="col-md-12">';
|
||||
break;
|
||||
case 2:
|
||||
$result .= '<div class="col-md-6">';
|
||||
break;
|
||||
case 3:
|
||||
$result .= '<div class="col-md-4">';
|
||||
break;
|
||||
case 4:
|
||||
$result .= '<div class="col-md-4">';
|
||||
break;
|
||||
case 5:
|
||||
$result .= '<div class="col-md-4">';
|
||||
break;
|
||||
default:
|
||||
$result .= '<div class="col-md-1">';
|
||||
break;
|
||||
}
|
||||
|
||||
foreach($cols as $col){
|
||||
}
|
||||
$result .= '</div>';
|
||||
}
|
||||
}
|
185
system/controllers/dashboard_widget.php
Normal file
185
system/controllers/dashboard_widget.php
Normal file
@ -0,0 +1,185 @@
|
||||
<?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');
|
||||
|
||||
$widgets = ORM::for_table('tbl_widgets')->selects("enabled", 1)->order_by_asc("orders")->findArray();
|
||||
$count = count($widgets);
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
try{
|
||||
require_once $WIDGET_PATH . DIRECTORY_SEPARATOR . $widgets[$i]['widget'].".php";
|
||||
$widgets[$i]['content'] = (new $widgets[$i]['widget'])->getWidget($widgets[$i]);;
|
||||
} catch (Throwable $e) {
|
||||
$widgets[$i]['content'] = $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
$ui->assign('widgets', $widgets);
|
||||
run_hook('view_dashboard'); #HOOK
|
||||
$ui->display('admin/dashboard_widget.tpl');
|
||||
die();
|
||||
|
||||
//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);
|
||||
|
||||
run_hook('view_dashboard'); #HOOK
|
||||
$ui->display('admin/dashboard.tpl');
|
102
system/controllers/widgets.php
Normal file
102
system/controllers/widgets.php
Normal file
@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
_admin();
|
||||
$ui->assign('_title', Lang::T('Widgets'));
|
||||
$ui->assign('_system_menu', 'settings');
|
||||
|
||||
$action = alphanumeric($routes['1']);
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
if ($action == 'add') {
|
||||
$pos = alphanumeric($routes['2']);
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
$orders = alphanumeric($_POST['orders']);
|
||||
$position = alphanumeric($_POST['position']);
|
||||
$enabled = alphanumeric($_POST['enabled']);
|
||||
$title = _post('title');
|
||||
$widget = _post('widget');
|
||||
$content = _post('content');
|
||||
$d = ORM::for_table('tbl_widgets')->create();
|
||||
$d->orders = $orders;
|
||||
$d->position = $position;
|
||||
$d->enabled = $enabled;
|
||||
$d->title = $title;
|
||||
$d->widget = $widget;
|
||||
$d->content = $content;
|
||||
$d->save();
|
||||
if ($d->id() > 0) {
|
||||
r2(getUrl('widgets'), 's', 'Widget Added Successfully');
|
||||
}
|
||||
}
|
||||
$files = scandir($WIDGET_PATH);
|
||||
$widgets = [];
|
||||
foreach ($files as $file) {
|
||||
if (strpos($file, '.php') !== false) {
|
||||
$name = ucwords(str_replace('.php', '', str_replace('_', ' ', $file)));
|
||||
$widgets[str_replace('.php', '', $file)] = $name;
|
||||
}
|
||||
}
|
||||
$widget['position'] = $pos;
|
||||
$ui->assign('do', 'add');
|
||||
$ui->assign('widgets', $widgets);
|
||||
$ui->assign('widget', $widget);
|
||||
$ui->display('admin/settings/widgets_add_edit.tpl');
|
||||
} else if ($action == 'edit') {
|
||||
// if request method post then save data
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
$id = alphanumeric($_POST['id']);
|
||||
$orders = alphanumeric($_POST['orders']);
|
||||
$position = alphanumeric($_POST['position']);
|
||||
$enabled = alphanumeric($_POST['enabled']);
|
||||
$title = _post('title');
|
||||
$widget = _post('widget');
|
||||
$content = _post('content');
|
||||
|
||||
$d = ORM::for_table('tbl_widgets')->find_one($id);
|
||||
$d->orders = $orders;
|
||||
$d->position = $position;
|
||||
$d->enabled = $enabled;
|
||||
$d->title = $title;
|
||||
$d->widget = $widget;
|
||||
$d->content = $content;
|
||||
$d->save();
|
||||
r2(getUrl('widgets'), 's', 'Widget Saved Successfully');
|
||||
}
|
||||
$id = alphanumeric($routes['2']);
|
||||
$widget = ORM::for_table('tbl_widgets')->find_one($id);
|
||||
$files = scandir($WIDGET_PATH);
|
||||
$widgets = [];
|
||||
foreach ($files as $file) {
|
||||
if (strpos($file, '.php') !== false) {
|
||||
$name = ucwords(str_replace('.php', '', str_replace('_', ' ', $file)));
|
||||
$widgets[str_replace('.php', '', $file)] = $name;
|
||||
}
|
||||
}
|
||||
$ui->assign('do', 'edit');
|
||||
$ui->assign('widgets', $widgets);
|
||||
$ui->assign('widget', $widget);
|
||||
$ui->display('admin/settings/widgets_add_edit.tpl');
|
||||
} else if ($action == 'delete') {
|
||||
$id = alphanumeric($routes['2']);
|
||||
$d = ORM::for_table('tbl_widgets')->find_one($id);
|
||||
if ($d) {
|
||||
$d->delete();
|
||||
r2(getUrl('widgets'), 's', 'Widget Deleted Successfully');
|
||||
}
|
||||
r2(getUrl('widgets'), 'e', 'Widget Not Found');
|
||||
} else if (!empty($action) && file_exists("system/widget/$action.php") && !empty($routes['2'])) {
|
||||
require_once "system/widget/$action.php";
|
||||
try {
|
||||
(new $action)->run_command($routes['2']);
|
||||
} catch (Throwable $e) {
|
||||
//nothing to do
|
||||
}
|
||||
} else {
|
||||
$widgets = ORM::for_table('tbl_widgets')->selects("position", 1)->order_by_asc("orders")->find_many();
|
||||
$ui->assign('widgets', $widgets);
|
||||
$ui->display('admin/settings/widgets.tpl');
|
||||
}
|
@ -1027,5 +1027,12 @@
|
||||
"Back_to_Dashboard": "Back to Dashboard",
|
||||
"Continue_the_VPN_creation_process_": "Continue the VPN creation process?",
|
||||
"VPN": "VPN",
|
||||
"Go_Back": "Go Back"
|
||||
"Go_Back": "Go Back",
|
||||
"Widgets": "Widgets",
|
||||
"Order": "Order",
|
||||
"Position": "Position",
|
||||
"Widget": "Widget",
|
||||
"Contents": "Contents",
|
||||
"Please_enter_a_search_term_": "Please enter a search term.",
|
||||
"Customers_Expired__Today": "Customers Expired, Today"
|
||||
}
|
30
system/widgets/customer_expired.php
Normal file
30
system/widgets/customer_expired.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
|
||||
class customer_expired
|
||||
{
|
||||
|
||||
|
||||
public function getWidget()
|
||||
{
|
||||
global $ui, $current_date;
|
||||
|
||||
//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);
|
||||
return $ui->fetch('widget/customer_expired.tpl');
|
||||
}
|
||||
}
|
22
system/widgets/html_php.php
Normal file
22
system/widgets/html_php.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
class html_php
|
||||
{
|
||||
|
||||
public function getWidget($data = null)
|
||||
{
|
||||
global $ui;
|
||||
$ui->assign('card_header', $data['title']);
|
||||
ob_start();
|
||||
try{
|
||||
eval('?>'. $data['content']);
|
||||
}catch(Exception $e){
|
||||
echo $e->getMessage();
|
||||
echo "<br>";
|
||||
echo $e->getTraceAsString();
|
||||
}
|
||||
$content = ob_get_clean();
|
||||
$ui->assign('card_body', $content);
|
||||
return $ui->fetch('widget/card_html.tpl');
|
||||
}
|
||||
}
|
18
system/widgets/template.md
Normal file
18
system/widgets/template.md
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```php
|
||||
class widget_name
|
||||
{
|
||||
|
||||
public static getWidget($data)
|
||||
{
|
||||
global $config, $ui;
|
||||
|
||||
return $ui->fetch('widget/template');
|
||||
}
|
||||
}
|
||||
```
|
56
system/widgets/top_widget.php
Normal file
56
system/widgets/top_widget.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
|
||||
class top_widget
|
||||
{
|
||||
public function getWidget()
|
||||
{
|
||||
global $config, $ui, $current_date, $start_date;
|
||||
|
||||
$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);
|
||||
return $ui->fetch('widget/top_widget.tpl');
|
||||
}
|
||||
}
|
66
ui/ui/admin/dashboard_widget.tpl
Normal file
66
ui/ui/admin/dashboard_widget.tpl
Normal file
@ -0,0 +1,66 @@
|
||||
{include file="sections/header.tpl"}
|
||||
|
||||
{function showWidget pos=0}
|
||||
{foreach $widgets as $w}
|
||||
{if $w['position'] == $pos}
|
||||
{$w['content']}
|
||||
{/if}
|
||||
{/foreach}
|
||||
{/function}
|
||||
|
||||
|
||||
|
||||
{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 $_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"}
|
@ -20,8 +20,10 @@
|
||||
<link rel="stylesheet" href="{$app_url}/ui/ui/styles/sweetalert2.min.css" />
|
||||
<link rel="stylesheet" href="{$app_url}/ui/ui/styles/plugins/pace.css" />
|
||||
<link rel="stylesheet" href="{$app_url}/ui/ui/summernote/summernote.min.css" />
|
||||
<script src="{$app_url}/ui/ui/scripts/sweetalert2.all.min.js"></script>
|
||||
<link rel="stylesheet" href="{$app_url}/ui/ui/styles/phpnuxbill.css?2025.2.4" />
|
||||
|
||||
<script src="{$app_url}/ui/ui/scripts/sweetalert2.all.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.5.1/dist/chart.min.js"></script>
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
73
ui/ui/admin/settings/widgets.tpl
Normal file
73
ui/ui/admin/settings/widgets.tpl
Normal file
@ -0,0 +1,73 @@
|
||||
{include file="sections/header.tpl"}
|
||||
|
||||
|
||||
{function showWidget pos=0}
|
||||
<form method="post" action="{Text::url('widgets/pos/')}">
|
||||
<div class="panel panel-info">
|
||||
<div class="panel-heading">Area {$pos}</div>
|
||||
<div class="panel-body">
|
||||
{foreach $widgets as $w}
|
||||
{if $w['position'] == $pos}
|
||||
<div class="panel panel-{if $w['enabled']}default{else}danger{/if}">
|
||||
<div class="panel-heading"><b>{$w['title']}</b></div>
|
||||
<div class="panel-body">{ucwords(str_replace('.php', '', str_replace('_', ' ', $w['widget'])))}</div>
|
||||
<table class="table table-bordered table-condensed">
|
||||
<tr>
|
||||
<td>
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon">orders</span>
|
||||
<input type="number" style="width: 100px;" name="orders[]" value="{$w['id']}"
|
||||
class="form-control" placeholder="orders">
|
||||
</div>
|
||||
<input type="hidden" name="id" value="{$w['id']}">
|
||||
</td>
|
||||
<td width="130">
|
||||
<div class="btn-group btn-group-justified" role="group">
|
||||
<a href="{Text::url('widgets/edit/', $w['id'])}" class="btn btn-sm btn-success">edit</a>
|
||||
<a href="{Text::url('widgets/delete/', $w['id'])}"
|
||||
onclick="return ask(this, 'Delete this widget?')" class="btn btn-sm btn-danger"><i
|
||||
class="glyphicon glyphicon-trash"></i></a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{/if}
|
||||
{/foreach}
|
||||
</div>
|
||||
<div class="panel-footer">
|
||||
<div class="btn-group btn-group-justified" role="group">
|
||||
<div class="btn-group" role="group">
|
||||
<button type="submit" class="btn btn-info">Save Orders</button>
|
||||
</div>
|
||||
<a href="{Text::url('widgets/add/', $pos)}" class="btn btn-primary">Add new widget</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{/function}
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
{showWidget widgets=$widgets pos=1}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-7 border">
|
||||
{showWidget widgets=$widgets pos=2}
|
||||
</div>
|
||||
<div class="col-md-5 border">
|
||||
{showWidget widgets=$widgets pos=3}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
{showWidget widgets=$widgets pos=4}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{include file="sections/footer.tpl"}
|
92
ui/ui/admin/settings/widgets_add_edit.tpl
Normal file
92
ui/ui/admin/settings/widgets_add_edit.tpl
Normal file
@ -0,0 +1,92 @@
|
||||
{include file="sections/header.tpl"}
|
||||
|
||||
<form method="post" action="{Text::url('widgets/', $do)}">
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<div class="panel panel-info">
|
||||
<div class="panel-heading">{if $do == 'add'}{Lang::T('Add')}{else}{Lang::T('Edit')}{/if} Widget</div>
|
||||
<div class="panel-body">
|
||||
<input type="hidden" class="form-control" required id="id" name="id" value="{$widget['id']}">
|
||||
<div class="form-group">
|
||||
<label class="col-md-3 control-label">{Lang::T('Title')}</label>
|
||||
<div class="col-md-5">
|
||||
<input type="text" class="form-control" required id="title" name="title"
|
||||
value="{$widget['title']}">
|
||||
</div>
|
||||
<span class="help-block col-md-4"> </span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-3 control-label">{Lang::T('Order')}</label>
|
||||
<div class="col-md-5">
|
||||
<input type="number" class="form-control" id="orders" name="orders"
|
||||
value="{if empty($widget['orders'])}99{else}{$widget['orders']}{/if}">
|
||||
</div>
|
||||
<span class="help-block col-md-4"> </span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<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 4}
|
||||
<option value="{$pos}" {if $widget['position'] eq $pos}selected="selected" {/if}>
|
||||
Area {$pos}</option>
|
||||
{/for}
|
||||
</select>
|
||||
</div>
|
||||
<p class="help-block col-md-4"> </p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-3 control-label">{Lang::T('Widget')}</label>
|
||||
<div class="col-md-5">
|
||||
<select name="widget" id="widget" class="form-control">
|
||||
{foreach $widgets as $k => $v}
|
||||
<option value="{$k}" {if $widget['widget'] eq $k}selected="selected" {/if}>
|
||||
{$v}
|
||||
</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
<p class="help-block col-md-4"> </p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-3 control-label">{Lang::T('Enabled')}</label>
|
||||
<div class="col-md-5">
|
||||
<select name="enabled" id="enabled" class="form-control">
|
||||
<option value="1" {if $widget['enabled'] neq 1}selected="selected" {/if}>
|
||||
{Lang::T('Active')}</option>
|
||||
<option value="0" {if $widget['enabled'] eq 0}selected="selected" {/if}>
|
||||
{Lang::T('Inactive')}</option>
|
||||
<option value="1" {if $widget['enabled'] neq 1}selected="selected" {/if}>
|
||||
{Lang::T('Active')}</option>
|
||||
</select>
|
||||
</div>
|
||||
<p class="help-block col-md-4"> </p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-3 control-label">{Lang::T('Contents')}</label>
|
||||
<div class="col-md-9">
|
||||
<textarea name="content" rows="5" id="content"
|
||||
class="form-control">{$widget['content']}</textarea>
|
||||
<p class="help-block">Not All Widget need contents, HTML widget need content, it can be text
|
||||
or PHP Code, be careful</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-footer">
|
||||
<div class="btn-group btn-group-justified" role="group">
|
||||
<a href="{Text::url('widgets/')}" class="btn btn-default btn-block">{Lang::T('Back')}</a>
|
||||
<div class="btn-group" role="group">
|
||||
<button type="submit" class="btn btn-info btn-block">{Lang::T('Save')}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{if $do == 'edit'}
|
||||
<a href="{Text::url('widgets/delete/', $widget['id'])}" class="btn btn-danger btn-xs"
|
||||
onclick="return ask(this, 'Delete this widget?')">{Lang::T('Delete')}</a>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{include file="sections/footer.tpl"}
|
14
ui/ui/widget/activity_log.tpl
Normal file
14
ui/ui/widget/activity_log.tpl
Normal file
@ -0,0 +1,14 @@
|
||||
<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>
|
5
ui/ui/widget/card_html.tpl
Normal file
5
ui/ui/widget/card_html.tpl
Normal file
@ -0,0 +1,5 @@
|
||||
<div class="panel panel-info panel-hovered mb20 activities">
|
||||
<div class="panel-heading">{$card_header}</div>
|
||||
<div class="panel-body">{$card_body}</ul>
|
||||
</div>
|
||||
</div>
|
19
ui/ui/widget/cron_monitor.tpl
Normal file
19
ui/ui/widget/cron_monitor.tpl
Normal file
@ -0,0 +1,19 @@
|
||||
{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}
|
33
ui/ui/widget/customer_expired.tpl
Normal file
33
ui/ui/widget/customer_expired.tpl
Normal file
@ -0,0 +1,33 @@
|
||||
<div class="panel panel-warning mb20 panel-hovered project-stats table-responsive">
|
||||
<div class="panel-heading">{Lang::T('Customers 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>
|
10
ui/ui/widget/default_info_row.tpl
Normal file
10
ui/ui/widget/default_info_row.tpl
Normal file
@ -0,0 +1,10 @@
|
||||
<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>
|
59
ui/ui/widget/graph_customers_insight.tpl
Normal file
59
ui/ui/widget/graph_customers_insight.tpl
Normal file
@ -0,0 +1,59 @@
|
||||
<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>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
{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>
|
73
ui/ui/widget/graph_monthly_registered_customers.tpl
Normal file
73
ui/ui/widget/graph_monthly_registered_customers.tpl
Normal file
@ -0,0 +1,73 @@
|
||||
<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>
|
||||
|
||||
|
||||
<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}
|
||||
</script>
|
81
ui/ui/widget/graph_monthly_sales.tpl
Normal file
81
ui/ui/widget/graph_monthly_sales.tpl
Normal file
@ -0,0 +1,81 @@
|
||||
<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>
|
||||
|
||||
<script type="text/javascript">
|
||||
{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}
|
||||
</script>
|
4
ui/ui/widget/info_payment_gateway.tpl
Normal file
4
ui/ui/widget/info_payment_gateway.tpl
Normal file
@ -0,0 +1,4 @@
|
||||
<div class="panel panel-success panel-hovered mb20 activities">
|
||||
<div class="panel-heading">{Lang::T('Payment Gateway')}: {str_replace(',',', ',$_c['payment_gateway'])}
|
||||
</div>
|
||||
</div>
|
19
ui/ui/widget/mikrotik_offline.tpl
Normal file
19
ui/ui/widget/mikrotik_offline.tpl
Normal file
@ -0,0 +1,19 @@
|
||||
{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}
|
50
ui/ui/widget/top_widget.tpl
Normal file
50
ui/ui/widget/top_widget.tpl
Normal file
@ -0,0 +1,50 @@
|
||||
<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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
30
ui/ui/widget/voucher_stocks.tpl
Normal file
30
ui/ui/widget/voucher_stocks.tpl
Normal file
@ -0,0 +1,30 @@
|
||||
{if $_c['disable_voucher'] != 'yes' && $stocks['unused']>0 || $stocks['used']>0}
|
||||
<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}
|
Loading…
x
Reference in New Issue
Block a user