';
+ break;
+ default:
+ $result .= '
';
+ break;
+ }
+
+ foreach($cols as $col){
+ }
+ $result .= '
';
+ }
+}
\ No newline at end of file
diff --git a/system/controllers/dashboard_widget.php b/system/controllers/dashboard_widget.php
new file mode 100644
index 00000000..a4937662
--- /dev/null
+++ b/system/controllers/dashboard_widget.php
@@ -0,0 +1,185 @@
+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');
diff --git a/system/controllers/widgets.php b/system/controllers/widgets.php
new file mode 100644
index 00000000..e74a8c33
--- /dev/null
+++ b/system/controllers/widgets.php
@@ -0,0 +1,102 @@
+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');
+}
diff --git a/system/lan/english.json b/system/lan/english.json
index 261d8081..2d153dc5 100644
--- a/system/lan/english.json
+++ b/system/lan/english.json
@@ -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"
}
\ No newline at end of file
diff --git a/system/widgets/customer_expired.php b/system/widgets/customer_expired.php
new file mode 100644
index 00000000..ede3e4f8
--- /dev/null
+++ b/system/widgets/customer_expired.php
@@ -0,0 +1,30 @@
+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');
+ }
+}
diff --git a/system/widgets/html_php.php b/system/widgets/html_php.php
new file mode 100644
index 00000000..dcbb9040
--- /dev/null
+++ b/system/widgets/html_php.php
@@ -0,0 +1,22 @@
+assign('card_header', $data['title']);
+ ob_start();
+ try{
+ eval('?>'. $data['content']);
+ }catch(Exception $e){
+ echo $e->getMessage();
+ echo "
";
+ echo $e->getTraceAsString();
+ }
+ $content = ob_get_clean();
+ $ui->assign('card_body', $content);
+ return $ui->fetch('widget/card_html.tpl');
+ }
+}
\ No newline at end of file
diff --git a/system/widgets/template.md b/system/widgets/template.md
new file mode 100644
index 00000000..3d3fe053
--- /dev/null
+++ b/system/widgets/template.md
@@ -0,0 +1,18 @@
+fetch('widget/template');
+ }
+}
+```
diff --git a/system/widgets/top_widget.php b/system/widgets/top_widget.php
new file mode 100644
index 00000000..3c6909a9
--- /dev/null
+++ b/system/widgets/top_widget.php
@@ -0,0 +1,56 @@
+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');
+ }
+}
diff --git a/ui/ui/admin/dashboard_widget.tpl b/ui/ui/admin/dashboard_widget.tpl
new file mode 100644
index 00000000..af3fba80
--- /dev/null
+++ b/ui/ui/admin/dashboard_widget.tpl
@@ -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}
+
+
+ {showWidget widgets=$widgets pos=2}
+
+
+ {showWidget widgets=$widgets pos=3}
+
+
+{showWidget widgets=$widgets pos=4}
+
+
+{if $_c['new_version_notify'] != 'disable'}
+
+{/if}
+
+{include file="sections/footer.tpl"}
\ No newline at end of file
diff --git a/ui/ui/admin/header.tpl b/ui/ui/admin/header.tpl
index 631d601f..f0c1fd3d 100644
--- a/ui/ui/admin/header.tpl
+++ b/ui/ui/admin/header.tpl
@@ -20,8 +20,10 @@
-
+
+
+
diff --git a/ui/ui/admin/settings/widgets.tpl b/ui/ui/admin/settings/widgets.tpl
new file mode 100644
index 00000000..b605e80a
--- /dev/null
+++ b/ui/ui/admin/settings/widgets.tpl
@@ -0,0 +1,73 @@
+{include file="sections/header.tpl"}
+
+
+{function showWidget pos=0}
+
+{/function}
+
+
+
+
+ {showWidget widgets=$widgets pos=1}
+
+
+
+
+ {showWidget widgets=$widgets pos=2}
+
+
+ {showWidget widgets=$widgets pos=3}
+
+
+
+
+
+ {showWidget widgets=$widgets pos=4}
+
+
+
+
+
+{include file="sections/footer.tpl"}
\ No newline at end of file
diff --git a/ui/ui/admin/settings/widgets_add_edit.tpl b/ui/ui/admin/settings/widgets_add_edit.tpl
new file mode 100644
index 00000000..79c96b3c
--- /dev/null
+++ b/ui/ui/admin/settings/widgets_add_edit.tpl
@@ -0,0 +1,92 @@
+{include file="sections/header.tpl"}
+
+
+
+{include file="sections/footer.tpl"}
\ No newline at end of file
diff --git a/ui/ui/widget/activity_log.tpl b/ui/ui/widget/activity_log.tpl
new file mode 100644
index 00000000..28dfc88a
--- /dev/null
+++ b/ui/ui/widget/activity_log.tpl
@@ -0,0 +1,14 @@
+
+
+
+
+ {foreach $dlog as $dlogs}
+
+
+ {Lang::timeElapsed($dlogs['date'],true)}
+ {$dlogs['description']}
+
+ {/foreach}
+
+
+
\ No newline at end of file
diff --git a/ui/ui/widget/card_html.tpl b/ui/ui/widget/card_html.tpl
new file mode 100644
index 00000000..39d924fb
--- /dev/null
+++ b/ui/ui/widget/card_html.tpl
@@ -0,0 +1,5 @@
+
+
{$card_header}
+
{$card_body}
+
+
\ No newline at end of file
diff --git a/ui/ui/widget/cron_monitor.tpl b/ui/ui/widget/cron_monitor.tpl
new file mode 100644
index 00000000..72258fac
--- /dev/null
+++ b/ui/ui/widget/cron_monitor.tpl
@@ -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}
+
+
{Lang::T('Cron has not run for over 1 hour. Please
+ check your setup.')}
+
+ {else}
+
+
{Lang::T('Cron Job last ran on')}: {$run_date}
+
+ {/if}
+{else}
+
+
{Lang::T('Cron appear not been setup, please check
+ your cron setup.')}
+
+{/if}
\ No newline at end of file
diff --git a/ui/ui/widget/customer_expired.tpl b/ui/ui/widget/customer_expired.tpl
new file mode 100644
index 00000000..dda37187
--- /dev/null
+++ b/ui/ui/widget/customer_expired.tpl
@@ -0,0 +1,33 @@
+
+
{Lang::T('Customers Expired, Today')}
+
+
+
+
+ {Lang::T('Username')}
+ {Lang::T('Created / Expired')}
+ {Lang::T('Internet Package')}
+ {Lang::T('Location')}
+
+
+
+ {foreach $expire as $expired}
+ {assign var="rem_exp" value="{$expired['expiration']} {$expired['time']}"}
+ {assign var="rem_started" value="{$expired['recharged_on']} {$expired['recharged_time']}"}
+
+ {$expired['username']}
+ {Lang::timeElapsed($rem_started)}
+ /
+ {Lang::timeElapsed($rem_exp)}
+
+ {$expired['namebp']}
+ {$expired['routers']}
+
+ {/foreach}
+
+
+
+ {include file="pagination.tpl"}
+
\ No newline at end of file
diff --git a/ui/ui/widget/default_info_row.tpl b/ui/ui/widget/default_info_row.tpl
new file mode 100644
index 00000000..7b83a9e6
--- /dev/null
+++ b/ui/ui/widget/default_info_row.tpl
@@ -0,0 +1,10 @@
+
+ {Lang::dateFormat($start_date)}
+ {Lang::dateFormat($current_date)}
+ {if $_c['enable_balance'] == 'yes' && in_array($_admin['user_type'],['SuperAdmin','Admin', 'Report'])}
+
+ {Lang::T('Customer Balance')} {$_c['currency_code']}
+ {number_format($cb,0,$_c['dec_point'],$_c['thousands_sep'])}
+
+ {/if}
+
\ No newline at end of file
diff --git a/ui/ui/widget/graph_customers_insight.tpl b/ui/ui/widget/graph_customers_insight.tpl
new file mode 100644
index 00000000..e94d8aea
--- /dev/null
+++ b/ui/ui/widget/graph_customers_insight.tpl
@@ -0,0 +1,59 @@
+
+
{Lang::T('All Users Insights')}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ui/ui/widget/graph_monthly_registered_customers.tpl b/ui/ui/widget/graph_monthly_registered_customers.tpl
new file mode 100644
index 00000000..0f4a682e
--- /dev/null
+++ b/ui/ui/widget/graph_monthly_registered_customers.tpl
@@ -0,0 +1,73 @@
+
+
+
+
\ No newline at end of file
diff --git a/ui/ui/widget/graph_monthly_sales.tpl b/ui/ui/widget/graph_monthly_sales.tpl
new file mode 100644
index 00000000..ec0b7684
--- /dev/null
+++ b/ui/ui/widget/graph_monthly_sales.tpl
@@ -0,0 +1,81 @@
+
+
+
\ No newline at end of file
diff --git a/ui/ui/widget/info_payment_gateway.tpl b/ui/ui/widget/info_payment_gateway.tpl
new file mode 100644
index 00000000..38f5b540
--- /dev/null
+++ b/ui/ui/widget/info_payment_gateway.tpl
@@ -0,0 +1,4 @@
+
+
{Lang::T('Payment Gateway')}: {str_replace(',',', ',$_c['payment_gateway'])}
+
+
\ No newline at end of file
diff --git a/ui/ui/widget/mikrotik_offline.tpl b/ui/ui/widget/mikrotik_offline.tpl
new file mode 100644
index 00000000..31e4dd0b
--- /dev/null
+++ b/ui/ui/widget/mikrotik_offline.tpl
@@ -0,0 +1,19 @@
+{if $_c['router_check'] && count($routeroffs)> 0}
+
+
{Lang::T('Routers Offline')}
+
+
+
+ {foreach $routeroffs as $ros}
+
+ {$ros['name']}
+ {Lang::timeElapsed($ros['last_seen'])}
+
+
+ {/foreach}
+
+
+
+
+{/if}
\ No newline at end of file
diff --git a/ui/ui/widget/top_widget.tpl b/ui/ui/widget/top_widget.tpl
new file mode 100644
index 00000000..d769b289
--- /dev/null
+++ b/ui/ui/widget/top_widget.tpl
@@ -0,0 +1,50 @@
+
+ {if in_array($_admin['user_type'],['SuperAdmin','Admin', 'Report'])}
+
+
+
+
{$_c['currency_code']}
+ {number_format($iday,0,$_c['dec_point'],$_c['thousands_sep'])}
+
+
+
+
+
+
+
+
+
+
+
{$_c['currency_code']}
+ {number_format($imonth,0,$_c['dec_point'],$_c['thousands_sep'])}
+
+
+
+
+
+
+
+ {/if}
+
+
+
+
{$u_act}/{$u_all-$u_act}
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ui/ui/widget/voucher_stocks.tpl b/ui/ui/widget/voucher_stocks.tpl
new file mode 100644
index 00000000..c3b0a7f1
--- /dev/null
+++ b/ui/ui/widget/voucher_stocks.tpl
@@ -0,0 +1,30 @@
+{if $_c['disable_voucher'] != 'yes' && $stocks['unused']>0 || $stocks['used']>0}
+
+
Vouchers Stock
+
+
+
+
+ {Lang::T('Package Name')}
+ unused
+ used
+
+
+
+ {foreach $plans as $stok}
+
+ {$stok['name_plan']}
+ {$stok['unused']}
+ {$stok['used']}
+
+
+ {/foreach}
+
+ Total
+ {$stocks['unused']}
+ {$stocks['used']}
+
+
+
+
+{/if}
\ No newline at end of file