Compare commits

...

18 Commits

Author SHA1 Message Date
3372da2ac4 2025.3.13 new Invoice print 2025-03-13 17:04:07 +07:00
803d04a91d add fullname to log extend 2025-03-13 15:59:55 +07:00
17de653752 paid icon 2025-03-13 15:22:25 +07:00
9301f1058c save pdf invoice customer from admin page 2025-03-13 15:07:15 +07:00
0868d61271 Download PDF invoice Customers 2025-03-13 15:07:15 +07:00
5f353392e3 Merge pull request #407 from ahmadhusein17/Development
Fix Text
2025-03-13 15:06:52 +07:00
fdd8dad509 Update indonesia.json 2025-03-12 00:29:50 +07:00
3563fa531b Update community.tpl
Minor fixes to make it look better
2025-03-12 00:24:11 +07:00
525f2311fc Update community.tpl
Update a bit to make it look better
2025-03-12 00:18:46 +07:00
3b6a6d2f55 Update recharge-confirm.tpl 2025-03-11 23:10:52 +07:00
3cebfa2171 Refactor login and registration templates to support dynamic logo, wallpaper, and favicon loading; change footer position to fixed 2025-03-11 15:50:06 +01:00
c65b569f94 Enhance mobile responsiveness and improve touch device support in login template 2025-03-11 09:20:00 +01:00
ca5a7d60cf API Rest Documentation? 2025-03-11 14:51:18 +07:00
5987ffafce Insomnia API Rest Collection 2025-03-11 14:39:56 +07:00
d7bbb4d18f Fix API request 2025-03-11 14:39:05 +07:00
e3c173bea4 Different expired message 2025-03-11 12:29:46 +07:00
78e1e2f989 add new Docs wiki 2025-03-11 12:28:22 +07:00
3c7e6c7a64 Different Reminder for PPPOE and Hotspot using <divider> 2025-03-11 11:55:48 +07:00
28 changed files with 1116 additions and 139 deletions

2
.gitignore vendored
View File

@ -55,3 +55,5 @@ docs/**
!docs/*.md
.htaccess
.idea
!docs/insomnia.rest.json
!system/uploads/paid.png

File diff suppressed because one or more lines are too long

1
docs/insomnia.rest.json Normal file

File diff suppressed because one or more lines are too long

View File

@ -48,7 +48,28 @@ $ui = new class($key)
}
function getAll()
{
return $this->assign;
$result = [];
foreach ($this->assign as $key => $value) {
if($value instanceof ORM){
$result[$key] = $value->as_array();
}else if($value instanceof IdiormResultSet){
$count = count($value);
for($n=0;$n<$count;$n++){
foreach ($value[$n] as $k=>$v) {
$result[$key][$n][$k] = $v;
}
}
}else{
$result[$key] = $value;
}
}
return $result;
}
function fetch()
{
return "";
}
};

View File

@ -22,8 +22,8 @@ class Csrf
public static function check($token)
{
global $config;
if($config['csrf_enabled'] == 'yes') {
global $config, $isApi;
if($config['csrf_enabled'] == 'yes' && !$isApi) {
if (isset($_SESSION['csrf_token'], $_SESSION['csrf_token_time'], $token)) {
$storedToken = $_SESSION['csrf_token'];
$tokenTime = $_SESSION['csrf_token_time'];

View File

@ -87,7 +87,7 @@ class File
$src_img = $image_create($source_file);
imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $nwidth, $nheight, $width, $height);
$image($dst_img, $dst_dir, $quality);
imagepng($dst_img, $dst_dir);
if ($dst_img) imagedestroy($dst_img);
if ($src_img) imagedestroy($src_img);

View File

@ -393,6 +393,18 @@ class Message
}
}
public static function getMessageType($type, $message){
if(strpos($message, "<divider>") === false){
return $message;
}
$msgs = explode("<divider>", $message);
if($type == "PPPOE"){
return $msgs[1];
}else{
return $msgs[0];
}
}
public static function logMessage($messageType, $recipient, $messageContent, $status, $errorMessage = null)
{
$log = ORM::for_table('tbl_message_logs')->create();

View File

@ -10,7 +10,7 @@ class Paginator
{
public static function findMany($query, $search = [], $per_page = '10', $append_url = "", $toArray = false)
{
global $routes, $ui;
global $routes, $ui, $isApi;
$adjacents = "2";
$page = _get('p', 1);
$page = (empty($page) ? 1 : $page);
@ -72,7 +72,7 @@ class Paginator
if ($ui) {
$ui->assign('paginator', $result);
}
if($toArray){
if($toArray || $isApi){
return $query->offset($startpoint)->limit($per_page)->find_array();
}else{
return $query->offset($startpoint)->limit($per_page)->find_many();

View File

@ -196,8 +196,8 @@ if (isset($_GET['recharge']) && !empty($_GET['recharge'])) {
$tur->save();
App::setToken(_get('stoken'), $id);
file_put_contents($path, $m);
_log("Customer $tur[customer_id] $tur[username] extend for $days days", "Customer", $user['id']);
Message::sendTelegram("#u$user[username] #extend #" . $p['type'] . " \n" . $p['name_plan'] .
_log("Customer $tur[customer_id] $user[fullname] ($tur[username]) extend for $days days", "Customer", $user['id']);
Message::sendTelegram("#u$user[username] ($user[fullname]) #id$tur[customer_id] #extend #" . $p['type'] . " \n" . $p['name_plan'] .
"\nLocation: " . $p['routers'] .
"\nCustomer: " . $user['fullname'] .
"\nNew Expired: " . Lang::dateAndTimeFormat($expiration, $tur['time']));

View File

@ -269,6 +269,17 @@ switch ($action) {
r2(getUrl('plan/view/') . $id, 'd', "Customer not found");
}
Package::createInvoice($in);
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
$logo = '';
if (file_exists($UPLOAD_PATH . DIRECTORY_SEPARATOR . 'logo.png')) {
$logo = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'logo.png';
$imgsize = getimagesize($logo);
$width = $imgsize[0];
$height = $imgsize[1];
$ui->assign('wlogo', $width);
$ui->assign('hlogo', $height);
}
$ui->assign('logo', $logo);
$ui->assign('_title', 'View Invoice');
$ui->display('admin/plan/invoice.tpl');
break;
@ -1062,11 +1073,11 @@ switch ($action) {
} else {
r2(getUrl('plan'), 's', "Customer not found");
}
Message::sendTelegram("#u$tur[username] #extend #" . $p['type'] . " \n" . $p['name_plan'] .
Message::sendTelegram("#u$tur[username] #id$tur[customer_id] #extend by $admin[fullname] #" . $p['type'] . " \n" . $p['name_plan'] .
"\nLocation: " . $p['routers'] .
"\nCustomer: " . $c['fullname'] .
"\nNew Expired: " . Lang::dateAndTimeFormat($expiration, $tur['time']));
_log("$admin[fullname] extend Customer $tur[customer_id] $tur[username] for $days days", $admin['user_type'], $admin['id']);
_log("$admin[fullname] extend Customer $tur[customer_id] $tur[username] #$tur[customer_id] for $days days", $admin['user_type'], $admin['id']);
r2(getUrl('plan'), 's', "Extend until $expiration");
} else {
r2(getUrl('plan'), 's', "Customer is not expired yet");

View File

@ -113,7 +113,8 @@ switch ($do) {
$d->save();
}
}
if (file_exists($_FILES['photo']['tmp_name'])) unlink($_FILES['photo']['tmp_name']);
if (file_exists($_FILES['photo']['tmp_name']))
unlink($_FILES['photo']['tmp_name']);
User::setFormCustomField($user);
run_hook('register_user'); #HOOK
$msg .= Lang::T('Registration successful') . '<br>';
@ -147,8 +148,45 @@ switch ($do) {
// Display register-otp.tpl if OTP is enabled
$ui->display('customer/register-otp.tpl');
} else {
// Display register.tpl if OTP is not enabled
$ui->display('customer/register.tpl');
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
if (!empty($config['login_page_logo']) && file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_logo'])) {
$login_logo = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_logo'];
} elseif (file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'login-logo.png')) {
$login_logo = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'login-logo.png';
} else {
$login_logo = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'login-logo.default.png';
}
if (!empty($config['login_page_wallpaper']) && file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_wallpaper'])) {
$wallpaper = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_wallpaper'];
} elseif (file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'wallpaper.png')) {
$wallpaper = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'wallpaper.png';
} else {
$wallpaper = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'wallpaper.default.png';
}
if (!empty($config['login_page_favicon']) && file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_favicon'])) {
$favicon = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_favicon'];
} elseif (file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'favicon.png')) {
$favicon = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'favicon.png';
} else {
$favicon = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'favicon.default.png';
}
$ui->assign('login_logo', $login_logo);
$ui->assign('wallpaper', $wallpaper);
$ui->assign('favicon', $favicon);
$ui->assign('csrf_token', $csrf_token);
$ui->assign('_title', Lang::T('Login'));
$ui->assign('customFields', User::getFormCustomField($ui, true));
switch ($config['login_page_type']) {
case 'custom':
$ui->display('customer/reg-login-custom-' . $config['login_Page_template'] . '.tpl');
break;
default:
$ui->display('customer/register.tpl');
break;
}
}
}
break;
@ -196,6 +234,36 @@ switch ($do) {
$ui->display('customer/register-rotp.tpl');
}
} else {
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
if (!empty($config['login_page_logo']) && file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_logo'])) {
$login_logo = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_logo'];
} elseif (file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'login-logo.png')) {
$login_logo = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'login-logo.png';
} else {
$login_logo = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'login-logo.default.png';
}
if (!empty($config['login_page_wallpaper']) && file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_wallpaper'])) {
$wallpaper = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_wallpaper'];
} elseif (file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'wallpaper.png')) {
$wallpaper = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'wallpaper.png';
} else {
$wallpaper = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'wallpaper.default.png';
}
if (!empty($config['login_page_favicon']) && file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_favicon'])) {
$favicon = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_favicon'];
} elseif (file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'favicon.png')) {
$favicon = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'favicon.png';
} else {
$favicon = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'favicon.default.png';
}
$ui->assign('login_logo', $login_logo);
$ui->assign('wallpaper', $wallpaper);
$ui->assign('favicon', $favicon);
$ui->assign('csrf_token', $csrf_token);
$ui->assign('_title', Lang::T('Login'));
$ui->assign('customFields', User::getFormCustomField($ui, true));
$ui->assign('username', "");
$ui->assign('fullname', "");
@ -204,7 +272,15 @@ switch ($do) {
$ui->assign('otp', false);
$ui->assign('_title', Lang::T('Register'));
run_hook('view_register'); #HOOK
$ui->display('customer/register.tpl');
switch ($config['login_page_type']) {
case 'custom':
$ui->display('customer/reg-login-custom-' . $config['login_Page_template'] . '.tpl');
break;
default:
$ui->display('customer/register.tpl');
break;
}
}
break;
}

View File

@ -64,6 +64,17 @@ switch ($action) {
}
if ($in) {
Package::createInvoice($in);
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
$logo = '';
if (file_exists($UPLOAD_PATH . DIRECTORY_SEPARATOR . 'logo.png')) {
$logo = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'logo.png';
$imgsize = getimagesize($logo);
$width = $imgsize[0];
$height = $imgsize[1];
$ui->assign('wlogo', $width);
$ui->assign('hlogo', $height);
}
$ui->assign('logo', $logo);
$ui->display('customer/invoice-customer.tpl');
} else {
r2(getUrl('voucher/list-activated'), 'e', Lang::T('Not Found'));

View File

@ -89,7 +89,13 @@ foreach ($d as $ds) {
// Send notification and update user status
try {
echo Message::sendPackageNotification($c, $u['namebp'], $p['price'], $textExpired, $config['user_notification_expired']) . "\n";
echo Message::sendPackageNotification(
$c,
$u['namebp'],
$p['price'],
Message::getMessageType($p['type'], $textExpired),
$config['user_notification_expired']
) . "\n";
$u->status = 'off';
$u->save();
} catch (Throwable $e) {

View File

@ -51,23 +51,40 @@ foreach ($d as $ds) {
}
if ($ds['expiration'] == $day7 && $config['notification_reminder_7day'] !== 'no') {
try {
echo Message::sendPackageNotification($c, $p['name_plan'], $price, Lang::getNotifText('reminder_7_day'), $config['user_notification_reminder']) . "\n";
echo Message::sendPackageNotification(
$c,
$p['name_plan'],
$price,
Message::getMessageType($p['type'], Lang::getNotifText('reminder_7_day')),
$config['user_notification_reminder']
) . "\n";
} catch (Exception $e) {
sendTelegram("Cron Reminder failed to send 7-day reminder to " . $ds['username'] . " Error: " . $e->getMessage());
}
} else if ($ds['expiration'] == $day3 && $config['notification_reminder_3day'] !== 'no') {
try {
echo Message::sendPackageNotification($c, $p['name_plan'], $price, Lang::getNotifText('reminder_3_day'), $config['user_notification_reminder']) . "\n";
echo Message::sendPackageNotification(
$c,
$p['name_plan'],
$price,
Message::getMessageType($p['type'], Lang::getNotifText('reminder_3_day')),
$config['user_notification_reminder']
) . "\n";
} catch (Exception $e) {
sendTelegram("Cron Reminder failed to send 3-day reminder to " . $ds['username'] . " Error: " . $e->getMessage());
}
} else if ($ds['expiration'] == $day1 && $config['notification_reminder_1day'] !== 'no') {
try {
echo Message::sendPackageNotification($c, $p['name_plan'], $price, Lang::getNotifText('reminder_1_day'), $config['user_notification_reminder']) . "\n";
echo Message::sendPackageNotification(
$c,
$p['name_plan'],
$price,
Message::getMessageType($p['type'], Lang::getNotifText('reminder_1_day')),
$config['user_notification_reminder']
) . "\n";
} catch (Exception $e) {
sendTelegram("Cron Reminder failed to send 1-day reminder to " . $ds['username'] . " Error: " . $e->getMessage());
}
}
}
}

View File

@ -1120,5 +1120,33 @@
"Delete_this_widget_": "Delete this widget?",
"Remove_this_widget_": "Remove this widget?",
"PPPoE_Remote_IP": "PPPoE Remote IP",
"Continue_the_Recharge_process": "Continue the Recharge process"
"Continue_the_Recharge_process": "Continue the Recharge process",
"Contributors": "Contributors",
"Discussions": "Discussions",
"Get_help_from_community": "Get help from community",
"Github_Discussions": "Github Discussions",
"Telegram_Group": "Telegram Group",
"Feedback_and_Bug_Report": "Feedback and Bug Report",
"Give_Feedback": "Give Feedback",
"Chat_with_me": "Chat with me",
"_50_Paid_Support": "$50 Paid Support",
"donation_confirmation_": "donation confirmation?",
"Or_ask_any_Donation_Alternative": "Or ask any Donation Alternative",
"Free_WhatsApp_Gateway_and_Telegram_Bot_creater": "Free WhatsApp Gateway and Telegram Bot creater",
"There_is_a_Telegram_bot_wizard_in_here": "There is a Telegram bot wizard in here",
"is_a_billing_Hotspot_and_PPPOE_for_Mikrotik_using_PHP_and_Mikrotik_API_to_comunicate_with_router__If_you_get_more_profit_with_this_application__please_donate_us_": "is a billing Hotspot and PPPOE for Mikrotik using PHP and Mikrotik API to comunicate with router. If you get more profit with this application, please donate us.",
"Watch_project": "Watch project",
"in_here": "in here",
"Install_Latest_Version": "Install Latest Version",
"Download_Latest_Version": "Download Latest Version",
"Select_Old_Version": "Select Old Version",
"Current_Changelog": "Current Changelog",
"Repo_Changelog": "Repo Changelog",
"If_you_Download_manual_the_update_file__sometime_update_change_database__after_uploading__click_this_button_to_update_database_structure_": "If you Download manual the update file, sometime update change database, after uploading, click this button to update database structure.",
"Update_Database": "Update Database",
"Credits": "Credits",
"Continue_the_process_of_sending_messages": "Continue the process of sending messages",
"Yours_Balances": "Yours Balances",
"Friend_username": "Friend username",
"This_will_sync_dan_send_Customer_active_package_to_Mikrotik": "This will sync dan send Customer active package to Mikrotik"
}

View File

@ -384,6 +384,7 @@
"Vouchers": "Voucher",
"Refill_Customer": "Isi Ulang Voucher",
"Recharge_Customer": "Isi Ulang Paket",
"Plan": "Paket",
"Plans": "Paket",
"PPPOE": "PPPOE",
"Bandwidth": "Bandwidth",
@ -911,5 +912,7 @@
"Mandatory_Fields": "Bidang yang wajib diisi",
"Single_Admin_Session": "Sesi Admin Tunggal",
"Mikrotik_SMS_Command": "Perintah SMS Mikrotik",
"Expired_Cronjob_Every_5_Minutes__Recommended_": "Cronjob Kedaluwarsa Setiap 5 Menit [Direkomendasikan]"
"Expired_Cronjob_Every_5_Minutes__Recommended_": "Cronjob Kedaluwarsa Setiap 5 Menit [Direkomendasikan]",
"Visit": "Kunjungi",
"sync": "Sinkron"
}

BIN
system/uploads/paid.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -11,6 +11,21 @@ class customer_expired
//user expire
$query = ORM::for_table('tbl_user_recharges')
->table_alias('tur')
->selects([
'tur.id',
'tur.username',
'c.fullname',
'c.phonenumber',
'c.email',
'tur.expiration',
'tur.time',
'tur.recharged_on',
'tur.recharged_time',
'tur.namebp',
'tur.routers'
])
->join('tbl_customers', ['tur.customer_id', '=', 'c.id'], 'c')
->where_lte('expiration', $current_date)
->order_by_desc('expiration');
$expire = Paginator::findMany($query);
@ -25,6 +40,7 @@ class customer_expired
// Assign the pagination HTML to the template variable
$ui->assign('expire', $expire);
$ui->assign('cookie', $_COOKIE);
return $ui->fetch('widget/customer_expired.tpl');
}
}

View File

@ -10,7 +10,7 @@
<div class="box box-hovered mb20 box-primary">
<div class="box-header">
<h3 class="box-title">{Lang::T('Contributors')}</h3>
<h3 class="box-title">{Lang::T('Contribution')} PHPNuxBill</h3>
</div>
<div class="box-body">
<a href="https://github.com/hotspotbilling/phpnuxbill/graphs/contributors" target="_blank">
@ -22,9 +22,9 @@
<div class="col-sm-6">
<div class="box box-hovered mb20 box-primary">
<div class="box-header">
<h3 class="box-title">{Lang::T('Discussions')}</h3>
<h3 class="box-title">{Lang::T('Discussion Get Help from the Community')}</h3>
</div>
<div class="box-body">{Lang::T('Get help from community')}</div>
<div class="box-body">{Lang::T('Join the discussion to find solutions and support from a community ready to help.')}</div>
<div class="box-footer">
<div class="btn-group btn-group-justified" role="group" aria-label="...">
<a href="https://github.com/hotspotbilling/phpnuxbill/discussions" target="_blank"
@ -35,18 +35,6 @@
</div>
</div>
</div>
<div class="box box-hovered mb20 box-primary">
<div class="box-header">
<h3 class="box-title">{Lang::T('')}Feedback</h3>
</div>
<div class="box-body">
{Lang::T('Feedback and Bug Report')}
</div>
<div class="box-footer">
<a href="https://github.com/hotspotbilling/phpnuxbill/issues" target="_blank"
class="btn btn-primary btn-sm btn-block"><i class="ion ion-chatboxes"></i> {Lang::T('Give Feedback')}</a>
</div>
</div>
</div>
</div>
@ -54,10 +42,9 @@
<div class="col-sm-6">
<div class="box box-hovered mb20 box-primary">
<div class="box-header">
<h3 class="box-title">Donasi</h3>
<h3 class="box-title">{Lang::T('Donations')} 🇮🇩</h3>
</div>
<div class="box-body">Untuk pengembangan lebih baik, donasi ke iBNuX, donasi akan membantu terus
pengembangan aplikasi</div>
<div class="box-body">{Lang::T('To support further development, please donate to iBNuX. Your donation will help ensure the continued development of this application.')}</div>
<div class="table-responsive">
<table class="table table-bordered table-striped">
<tbody>
@ -89,10 +76,10 @@
<div class="col-sm-6">
<div class="box box-hovered mb20 box-primary">
<div class="box-header">
<h3 class="box-title">Donations</h3>
<h3 class="box-title">{Lang::T('Donations other than')} 🇮🇩</h3>
</div>
<div class="box-body">
Donations will help to continue phpnuxbill development
{Lang::T('Your donation will help support and continue the development of PHPNuxBill.')}
</div>
<div class="table-responsive">
<table class="table table-bordered table-striped">
@ -129,19 +116,21 @@
<div class="col-sm-6">
<div class="box box-hovered mb20 box-primary">
<div class="box-header">
<h3 class="box-title">{Lang::T('Chat with me')}</h3>
<h3 class="box-title">{Lang::T('Chat with Me — Paid Support $50')}</h3>
</div>
<div class="box-body">
{Lang::T('Confirm your donation to continue this paid support. Or, ask about alternative donations available to suit your needs.')}
</div>
<div class="box-body">{Lang::T('$50 Paid Support')}<br>{Lang::T('donation confirmation?')}<br>{Lang::T('Or ask any Donation Alternative')}</div>
<div class="box-footer">
<a href="https://t.me/ibnux" target="_blank" class="btn btn-primary btn-sm btn-block">Telegram</a>
<a href="https://t.me/ibnux" target="_blank" class="btn btn-primary btn-sm btn-block">{Lang::T('Telegram')}</a>
</div>
</div>
<div class="box box-primary box-hovered mb20 activities">
<div class="box-header">
<h3 class="box-title">{Lang::T('Free WhatsApp Gateway and Telegram Bot creater')}</h3>
<h3 class="box-title">{Lang::T('WhatsApp Gateway and Free Telegram Bot')}</h3>
</div>
<div class="box-body">
{Lang::T('There is a Telegram bot wizard in here')}
{Lang::T('Connect your PHPNuxBill to WhatsApp efficiently using WhatsApp Gateway. Also, create Telegram bots easily and practically.')}
</div>
<div class="box-footer">
<a href="https://wa.nux.my.id/login" target="_blank"
@ -155,35 +144,128 @@
<h3 class="box-title">PHPNUXBILL</h3>
</div>
<div class="box-body">
<b>PHPNuxBill</b> {Lang::T('is a billing Hotspot and PPPOE for Mikrotik using PHP and Mikrotik API to comunicate
with router. If you get more profit with this application, please donate us.')}<br>{Lang::T('Watch project')} <a
href="https://github.com/hotspotbilling/phpnuxbill" target="_blank">{Lang::T('in here')}</a>
<b>PHPNuxBill</b>
{Lang::T('is a Hotspot and PPPoE billing platform for Mikrotik developed using PHP. The application uses Mikrotik API to communicate with the router, ensuring efficient and easy integration. If you feel you get more benefits from this application, we would greatly appreciate your contribution through donation.')}<br>{Lang::T('Watch project ')} <a
href="https://github.com/hotspotbilling/phpnuxbill" target="_blank">{Lang::T('IN HERE')}</a>
</div>
<div class="box-footer" id="currentVersion">ver</div>
<div class="box-footer" id="latestVersion">ver</div>
<div class="box-footer">
<div class="btn-group btn-group-justified" role="group" aria-label="...">
<a href="./update.php" class="btn btn-success btn-sm btn-block">{Lang::T('Install Latest Version')}</a>
<a href="./update.php"
class="btn btn-success btn-sm btn-block">{Lang::T('Install Latest Version')}</a>
<a href="https://github.com/hotspotbilling/phpnuxbill/archive/refs/heads/master.zip" target="_blank"
class="btn btn-warning btn-sm btn-block text-black">{Lang::T('Download Latest Version')}</a>
</div>
<center><a href="{Text::url('community/rollback')}" class="btn btn-link btn-sm btn-block">{Lang::T('Select Old Version')}</a>
<center><a href="{Text::url('community/rollback')}"
class="btn btn-link btn-sm btn-block">{Lang::T('Select Old Version')}</a>
</center>
</div>
<div class="box-footer">
<div class="btn-group btn-group-justified" role="group" aria-label="...">
<a href="./CHANGELOG.md" target="_blank" class="btn btn-default btn-sm btn-block">{Lang::T('Current Changelog')}</a>
<a href="./CHANGELOG.md" target="_blank"
class="btn btn-default btn-sm btn-block">{Lang::T('Current Changelog')}</a>
<a href="https://github.com/hotspotbilling/phpnuxbill/blob/master/CHANGELOG.md" target="_blank"
class="btn btn-default btn-sm btn-block">{Lang::T('Repo Changelog')}</a>
</div>
</div>
<div class="box-footer">
{Lang::T('If you Download manual the update file, sometime update change database, after uploading, click this
button to update database structure.')}
{Lang::T('If you download the update file manually, sometimes the update may change the database structure. After the file is successfully uploaded, click this button to update the database structure.')}
<a href="./update.php?step=4" class="btn btn-default btn-sm btn-block">{Lang::T('Update Database')}</a>
</div>
</div>
<div class="box box-hovered mb20 box-primary">
<div class="box-header">
<h3 class="box-title">{Lang::T('Credits')}</h3>
</div>
<div class="box-body">
<div class="table-responsive">
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>{Lang::T('Souce')}</th>
<th>{Lang::T('Details')}</th>
</tr>
</thead>
<tbody>
<tr>
<td>Bootstrap V3</td>
<td>
<a href="https://getbootstrap.com/docs/3.4/" target="_blank">
<i class="glyphicon glyphicon-globe"></i> {Lang::T('Visit')}
</a>
</td>
</tr>
<tr>
<td>Admin LTE V3</td>
<td>
<a href="https://adminlte.io/themes/v3/" target="_blank">
<i class="glyphicon glyphicon-globe"></i> {Lang::T('Visit')}
</a>
</td>
</tr>
<tr>
<td>Smarty Template V4</td>
<td>
<a href="https://www.smarty.net/" target="_blank">
<i class="glyphicon glyphicon-globe"></i> {Lang::T('Visit')}
</a>
</td>
</tr>
<tr>
<td>PHP IdiORM</td>
<td>
<a href="https://idiorm.readthedocs.io/" target="_blank">
<i class="glyphicon glyphicon-globe"></i> {Lang::T('Visit')}
</a>
</td>
</tr>
<tr>
<td>PHP mPDF</td>
<td>
<a href="https://mpdf.github.io/" target="_blank">
<i class="glyphicon glyphicon-globe"></i> {Lang::T('Visit')}
</a>
</td>
</tr>
<tr>
<td>PHP QRCode</td>
<td>
<a href="http://phpqrcode.sourceforge.net/" target="_blank">
<i class="glyphicon glyphicon-globe"></i> {Lang::T('Visit')}
</a>
</td>
</tr>
<tr>
<td>PHP Net_RouterOS</td>
<td>
<a href="https://github.com/pear2/Net_RouterOS" target="_blank">
<i class="glyphicon glyphicon-globe"></i> {Lang::T('Visit')}
</a>
</td>
</tr>
<tr>
<td>Summernote</td>
<td>
<a href="https://summernote.org/" target="_blank">
<i class="glyphicon glyphicon-globe"></i> {Lang::T('Visit')}
</a>
</td>
</tr>
<tr>
<td>PHP Mailer</td>
<td>
<a href="https://github.com/PHPMailer/PHPMailer/" target="_blank">
<i class="glyphicon glyphicon-globe"></i> {Lang::T('Visit')}
</a>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<script>
window.addEventListener('DOMContentLoaded', function() {

View File

@ -90,7 +90,7 @@
<label class="col-md-3 control-label">{Lang::T('Coordinates')}</label>
<div class="col-md-9">
<input name="coordinates" id="coordinates" class="form-control" value=""
placeholder="6.465422, 3.406448">
placeholder="-6.465422, 3.406448">
<div id="map" style="width: '100%'; height: 200px; min-height: 150px;"></div>
</div>
</div>
@ -98,9 +98,11 @@
<div class="panel-heading">PPPoE</div>
<div class="panel-body">
<div class="form-group">
<label class="col-md-3 control-label">{Lang::T('Usernames')} <span class="label label-danger" id="warning_username"></span></label>
<label class="col-md-3 control-label">{Lang::T('Usernames')} <span class="label label-danger"
id="warning_username"></span></label>
<div class="col-md-9">
<input type="username" class="form-control" id="pppoe_username" name="pppoe_username" onkeyup="checkUsername(this, '0')">
<input type="username" class="form-control" id="pppoe_username" name="pppoe_username"
onkeyup="checkUsername(this, '0')">
<span class="help-block">{Lang::T('Not Working for freeradius')}</span>
</div>
</div>
@ -112,9 +114,11 @@
</div>
</div>
<div class="form-group">
<label class="col-md-3 control-label">Remote IP <span class="label label-danger" id="warning_ip"></span></label>
<label class="col-md-3 control-label">Remote IP <span class="label label-danger"
id="warning_ip"></span></label>
<div class="col-md-9">
<input type="text" class="form-control" id="pppoe_ip" name="pppoe_ip" onkeyup="checkIP(this, '0')">
<input type="text" class="form-control" id="pppoe_ip" name="pppoe_ip"
onkeyup="checkIP(this, '0')">
<span class="help-block">{Lang::T('Also Working for freeradius')}</span>
</div>
</div>
@ -204,7 +208,8 @@
</div>
</div>
<center>
<button class="btn btn-primary" onclick="return ask(this, '{Lang::T("Continue the process of adding Customer Data?")}')" type="submit">
<button class="btn btn-primary"
onclick="return ask(this, '{Lang::T("Continue the process of adding Customer Data?")}')" type="submit">
{Lang::T('Save Changes')}
</button>
<br><a href="{Text::url('customers/list')}" class="btn btn-link">{Lang::T('Cancel')}</a>

View File

@ -85,22 +85,32 @@
listAtts.forEach(function(el) {
if (el.addEventListener) { // all browsers except IE before version 9
el.addEventListener("click", function() {
var txt = $(this).html();
$(this).html(
`<span class="loading"></span>`
);
setTimeout(() => {
$(this).prop("disabled", true);
}, 100);
setTimeout(() => {
$(this).html(txt);
$(this).prop("disabled", false);
}, 5000);
}, false);
} else {
if (el.attachEvent) { // IE before version 9
el.attachEvent("click", function() {
var txt = $(this).html();
$(this).html(
`<span class="loading"></span>`
);
setTimeout(() => {
$(this).prop("disabled", true);
}, 100);
setTimeout(() => {
$(this).html(txt);
$(this).prop("disabled", false);
}, 5000);
});
}
}

View File

@ -1,16 +1,22 @@
{include file="sections/header.tpl"}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.3.4/jspdf.min.js"></script>
<div class="row">
<div class="col-md-6 col-sm-12 col-md-offset-3">
<div class="panel panel-hovered panel-primary panel-stacked mb30">
<div class="panel-heading">{$in['invoice']}</div>
<div class="panel-body">
{if !empty($logo)}
<center><img src="{$app_url}/{$logo}?"></center>
{/if}
<form class="form-horizontal" method="post" action="{Text::url('')}plan/print" target="_blank">
<pre id="content" style="text-align: center;"></pre>
<pre id="content"
style="border: 0px; ;text-align: center; background-color: transparent; background-image: url('{$app_url}/system/uploads/paid.png');background-repeat:no-repeat;background-position: center"></pre>
<textarea class="hidden" id="formcontent" name="content">{$invoice}</textarea>
<input type="hidden" name="id" value="{$in['id']}">
<a href="{Text::url('')}plan/list" class="btn btn-default btn-sm"><i
<a href="{Text::url('plan/list')}" class="btn btn-default btn-sm"><i
class="ion-reply-all"></i>{Lang::T('Finish')}</a>
<a href="javascript:download()" class="btn btn-success btn-sm text-black">
<i class="glyphicon glyphicon-share"></i> Download</a>
<a href="https://api.whatsapp.com/send/?text={$whatsapp}" target="_blank"
class="btn btn-primary btn-sm">
<i class="glyphicon glyphicon-share"></i> WhatsApp</a>
@ -38,7 +44,42 @@
</div>
</div>
</div>
<script type="text/javascript">
<script>
const canvas = document.createElement("canvas");
const ctx = canvas.getContext('2d');
ctx.font = '16px Courier';
var text = document.getElementById("formcontent").innerHTML;
var lines = text.split(/\r\n|\r|\n/).length;
var meas = ctx.measureText("A");
let width = Math.round({$_c['printer_cols']} * 9.6);
var height = Math.round((14 * lines));
console.log(width, height, lines);
var paid = new Image();
paid.src = '{$app_url}/system/uploads/paid.png';
{if !empty($logo)}
var img = new Image();
img.src = '{$app_url}/{$logo}';
var new_width = (width / 4) * 2;
var new_height = Math.ceil({$hlogo} * (new_width/{$wlogo}));
height = height + new_height;
{/if}
function download() {
var doc = new jsPDF('p', 'px', [width, height]);
{if !empty($logo)}
try {
doc.addImage(img, 'PNG', (width - new_width) / 2, 10, new_width, new_height);
} catch (err) {}
{/if}
try {
doc.addImage(paid, 'PNG', (width - 200) / 2, (height - 145) / 2, 200, 145);
} catch (err) {}
doc.setFont("Courier");
doc.setFontSize(16);
doc.text($('#formcontent').html(), width / 2, new_height + 30, 'center');
doc.save('{$in['invoice']}.pdf');
}
var s5_taf_parent = window.location;
document.getElementById('content').innerHTML = document.getElementById('formcontent').innerHTML;
</script>

View File

@ -55,7 +55,7 @@
{$plan['validity_unit']}</span>
</li>
<li class="list-group-item">
<b>{Lang::T('Using')}</b> <span class="pull-right">
<b>{Lang::T('Payment via')}</b> <span class="pull-right">
<select name="using"
style="background-color: white;outline: 1px;border: 1px solid #b7b7b7;">
{foreach $usings as $us}

View File

@ -1,21 +1,62 @@
{include file="customer/header.tpl"}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.3.4/jspdf.min.js"></script>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<div class="panel panel-hovered panel-primary panel-stacked mb30">
<div class="panel-heading">{$in['invoice']}</div>
<div class="panel-body">
{if !empty($logo)}
<center><img src="{$app_url}/{$logo}"></center>
{/if}
<form class="form-horizontal" method="post" action="{Text::url('plan/print')}" target="_blank">
<pre id="content" style="text-align: center;">{$invoice}</pre>
<pre id="content"
style="border: 0px; ;text-align: center; background-color: transparent; background-image: url('{$app_url}/system/uploads/paid.png');background-repeat:no-repeat;background-position: center">{$invoice}</pre>
<input type="hidden" name="id" value="{$in['id']}">
<a href="{Text::url('voucher/list-activated')}" class="btn btn-default btn-sm"><i
class="ion-reply-all"></i>{Lang::T('Finish')}</a>
<a href="https://api.whatsapp.com/send/?text={$whatsapp}" target="_blank"
class="btn btn-primary btn-sm">
<a href="javascript:download()" class="btn btn-success btn-sm text-black">
<i class="glyphicon glyphicon-share"></i> Download</a>
<a href="https://api.whatsapp.com/send/?text={$whatsapp}" class="btn btn-primary btn-sm">
<i class="glyphicon glyphicon-share"></i> WhatsApp</a>
</form>
</div>
</div>
</div>
</div>
<script>
const canvas = document.createElement("canvas");
const ctx = canvas.getContext('2d');
ctx.font = '16px Courier';
var text = document.getElementById("content").innerHTML;
var lines = text.split(/\r\n|\r|\n/).length;
var meas = ctx.measureText("A");
let width = Math.round({$_c['printer_cols']} * 9.6);
var height = Math.round((14 * lines));
console.log(width, height, lines);
var paid = new Image();
paid.src = '{$app_url}/system/uploads/paid.png';
{if !empty($logo)}
var img = new Image();
img.src = '{$app_url}/{$logo}';
var new_width = (width / 4) * 2;
var new_height = Math.ceil({$hlogo} * (new_width/{$wlogo}));
height = height + new_height;
{/if}
function download() {
var doc = new jsPDF('p', 'px', [width, height]);
{if !empty($logo)}
try {
doc.addImage(img, 'PNG', (width - new_width) / 2, 10, new_width, new_height);
} catch (err) {}
{/if}
try {
doc.addImage(paid, 'PNG', (width - 200) / 2, (height - 145) / 2, 200, 145);
} catch (err) {}
doc.setFont("Courier");
doc.setFontSize(16);
doc.text($('#content').html(), width / 2, new_height + 30, 'center');
doc.save('{$in['invoice']}.pdf');
}
</script>
{include file="customer/footer.tpl"}

View File

@ -3,13 +3,12 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>{$_title} - {$_c['CompanyName']}</title>
<link rel="shortcut icon" href="./{$favicon}" type="image/x-icon" />
<link rel='stylesheet' href='https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css' />
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css' />
<!-- SweetAlert CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css" />
<!-- SweetAlert JS -->
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.all.min.js"></script>
<style>
.login-fg .form-container {
@ -364,28 +363,6 @@
color: #db4437;
}
@media (max-width: 1200px) {
.login-fg .info h1 {
font-size: 45px;
}
}
@media (max-width: 992px) {
.login-fg .bg {
display: none;
}
}
@media (max-width: 768px) {
.login-fg .login-section .social li a {
width: 100px;
}
.login-fg .logo a {
font-size: 26px;
}
}
footer {
position: absolute;
bottom: 0;
@ -429,6 +406,47 @@
background-color: #00f2fe;
}
.submit-btn {
transition: all 0.3s ease;
-webkit-tap-highlight-color: transparent;
}
.submit-btn:active {
transform: scale(0.98);
}
input {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
.form-group {
margin-bottom: 15px;
}
.logo img {
max-width: 100%;
height: auto;
}
input[type="text"],
input[type="password"] {
inputmode: "verbatim";
}
@media (max-width: 1200px) {
.login-fg .info h1 {
font-size: 45px;
}
}
@media (max-width: 992px) {
.login-fg .bg {
display: none;
}
}
@media (max-width: 768px) {
input,
@ -436,14 +454,76 @@
padding: 12px;
font-size: 15px;
}
.login-fg .login-section {
padding: 15px;
max-width: 100%;
}
.login-fg .logo img {
max-width: 200px;
height: auto;
}
.login-fg .info h1 {
font-size: 32px;
margin-bottom: 10px;
}
.login-fg .info p {
font-size: 14px;
line-height: 1.5;
}
footer {
position: fixed;
margin-top: 20px;
padding: 8px;
font-size: 12px;
}
.login-fg .login-section h4 {
font-size: 18px;
margin-bottom: 20px;
}
.login-fg .login-section .social li a {
width: 100%;
margin: 5px 0;
}
.checkbox a {
display: block;
text-align: right;
margin-top: 10px;
}
.login-fg .login-section .social li a {
width: 100px;
}
.login-fg .logo a {
font-size: 26px;
}
}
@media (max-width: 480px) {
.login-fg .login {
padding: 15px;
}
input,
.submit-btn {
padding: 10px;
font-size: 14px;
font-size: 15px;
}
.login-fg .login-section h4 {
font-size: 16px;
}
footer {
font-size: 11px;
}
}
@ -460,18 +540,18 @@
<body>
{if isset($notify)}
<script>
// Display SweetAlert toast notification
document.body.style.overflow = 'hidden';
Swal.fire({
icon: '{if $notify_t == "s"}success{else}warning{/if}',
title: '{if $notify_t == "s"}Success{else}Error{/if}',
text: '{$notify}',
backdrop: 'rgba(0, 0, 0, 0.5)',
}).then(() => {
document.body.style.overflow = '';
});
</script>
<script>
// Display SweetAlert toast notification
document.body.style.overflow = 'hidden';
Swal.fire({
icon: '{if $notify_t == "s"}success{else}warning{/if}',
title: '{if $notify_t == "s"}Success{else}Error{/if}',
text: '{$notify}',
backdrop: 'rgba(0, 0, 0, 0.5)',
}).then(() => {
document.body.style.overflow = '';
});
</script>
{/if}
<!-- partial:index.partial.html -->
<div class="login-fg">
@ -530,8 +610,8 @@
</form>
</div>
{if $_c['disable_registration'] != 'noreg'}
<p>{Lang::T('Don\'t have an account?')} <a href="{Text::url('register')}"
class="linkButton">{Lang::T('Register')}</a></p>
<p>{Lang::T('Don\'t have an account?')} <a href="{Text::url('register')}"
class="linkButton">{Lang::T('Register')}</a></p>
{/if}
<footer>
© {$smarty.now|date_format:"%Y"} {$_c['CompanyName']}. All rights reserved. <br> <a
@ -551,25 +631,25 @@
const loginBtn = document.getElementById('login-btn');
const loginText = document.getElementById('login-text');
loginForm.addEventListener('submit', function(event) {
loginForm.addEventListener('submit', function (event) {
loginBtn.classList.add('loading');
loginText.textContent = 'Please Wait...';
});
</script>
{if $_c['tawkto'] != ''}
<!--Start of Tawk.to Script-->
<script type="text/javascript">
var isLoggedIn = false;
var Tawk_API = {
onLoad: function() {
if (!isLoggedIn) {
isLoggedIn = true;
window.Tawk_API.login({
name: '{$_user['fullname']}',
email: '{$_user['email']}',
userId: '{$_user['id']}'
}, function(error) {
//do something if there's an error
<!--Start of Tawk.to Script-->
<script type="text/javascript">
var isLoggedIn = false;
var Tawk_API = {
onLoad: function () {
if (!isLoggedIn) {
isLoggedIn = true;
window.Tawk_API.login({
name: '{$_user['fullname']}',
email: '{$_user['email']}',
userId: '{$_user['id']}'
}, function (error) {
//do something if there's an error
});
}
Tawk_API.setAttributes({
@ -579,7 +659,7 @@
'balance': '{$_user['balance']}',
'account_type': '{$_user['account_type']}',
'phone': '{$_user['phonenumber']}'
}, function(error) {});
}, function (error) { });
}
};
var Tawk_LoadStart = new Date();
@ -588,18 +668,44 @@
email: '{$_user['email']}',
userId: '{$_user['id']}'
};
(function() {
(function () {
var s1 = document.createElement("script"),
s0 = document.getElementsByTagName("script")[0];
s1.async = true;
s1.src = 'https://embed.tawk.to/{$_c['tawkto']}';
s1.charset = 'UTF-8';
s1.setAttribute('crossorigin', '*');
s0.parentNode.insertBefore(s1, s0);
})();
</script>
<!--End of Tawk.to Script-->
s0.parentNode.insertBefore(s1, s0);
})();
</script>
<!--End of Tawk.to Script-->
{/if}
<script>
document.body.classList.add('touch-device');
</script>
<script>
document.addEventListener('DOMContentLoaded', function () {
let metaViewport = document.querySelector('meta[name="viewport"]');
const originalContent = metaViewport.content;
document.querySelectorAll('input').forEach(input => {
input.addEventListener('focus', () => {
metaViewport.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0';
});
input.addEventListener('blur', () => {
metaViewport.content = originalContent;
});
});
// Touch detection
if ('ontouchstart' in window) {
document.body.classList.add('touch-device');
} else {
document.body.classList.add('no-touch');
}
});
</script>
</body>
</html>

View File

@ -0,0 +1,448 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>{$_title} - {$_c['CompanyName']}</title>
<link rel="shortcut icon" href="./{$favicon}" type="image/x-icon" />
<link rel='stylesheet' href='https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css' />
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css' />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css" />
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.all.min.js"></script>
<style>
.login-fg .form-container {
color: #ccc;
position: relative;
}
.login-fg .login {
min-height: 100vh;
position: relative;
display: flex;
justify-content: center;
align-items: center;
padding: 30px 15px;
}
.login-fg .login-section {
max-width: 370px;
margin: 0 auto;
text-align: center;
width: 100%;
}
.login-fg .form-fg {
width: 100%;
text-align: center;
}
.login-fg .form-container .form-group {
margin-bottom: 25px;
}
.login-fg .form-container .input-text {
font-size: 14px;
outline: none;
color: #616161;
border-radius: 3px;
font-weight: 500;
border: 1px solid transparent;
background: #fff;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
}
.login-fg .form-container img {
margin-bottom: 5px;
height: 40px;
}
.login-fg .form-container .form-fg input {
float: left;
width: 100%;
padding: 11px 45px 11px 20px;
border-radius: 50px;
}
.login-fg .form-container .form-fg i {
position: absolute;
top: 13px;
right: 20px;
font-size: 19px;
color: #616161;
}
.login-fg .form-container label {
font-weight: 500;
font-size: 14px;
margin-bottom: 5px;
}
.login-fg .form-container .btn-md {
cursor: pointer;
padding: 10px 30px 9px;
height: 45px;
letter-spacing: 1px;
font-size: 14px;
font-weight: 400;
font-family: "Open Sans", sans-serif;
border-radius: 50px;
color: #d6d6d6;
}
.login-fg .form-container .btn-fg {
background: #0f96f9;
border: none;
color: #fff;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
}
.login-fg .form-container .btn-fg:hover {
background: #108ae4;
}
footer {
position: absolute;
bottom: 0;
left: 0;
right: 0;
text-align: center;
font-size: 14px;
color: inherit;
background-color: #f8f8f8;
padding: 10px;
}
input {
width: 100%;
padding: 10px;
margin: 1px 0;
border: 1px solid #ccc;
border-radius: 5px;
font-size: 16px;
box-sizing: border-box;
}
input:focus {
border-color: #4facfe;
outline: none;
}
.submit-btn {
background-color: #4facfe;
color: white;
border: none;
padding: 15px;
border-radius: 5px;
font-size: 16px;
cursor: pointer;
width: 100%;
box-sizing: border-box;
}
.submit-btn:hover {
background-color: #00f2fe;
}
.submit-btn {
transition: all 0.3s ease;
-webkit-tap-highlight-color: transparent;
}
.submit-btn:active {
transform: scale(0.98);
}
.login-section {
max-height: 100vh;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
-ms-overflow-style: -ms-autohiding-scrollbar;
scrollbar-width: thin;
scrollbar-color: #888 #f1f1f1;
}
.login-section::-webkit-scrollbar {
width: 8px;
}
.login-section::-webkit-scrollbar-track {
background: #f1f1f1;
}
.login-section::-webkit-scrollbar-thumb {
background: #888;
border-radius: 4px;
}
.login-section::-webkit-scrollbar-thumb:hover {
background: #555;
}
.login-fg .info h1 {
font-size: 60px;
color: #fff;
font-weight: 700;
margin-bottom: 15px;
text-transform: uppercase;
text-shadow: 2px 0px #000;
}
.login-fg .info p {
margin-bottom: 0;
color: #fff;
line-height: 28px;
text-shadow: 1px 1px #000;
}
.login-fg .info {
text-align: center;
position: fixed;
top: 50%;
left: 33%;
transform: translate(-50%, -50%);
z-index: 1;
}
@media (max-width: 768px) {
input,
.submit-btn {
padding: 12px;
font-size: 15px;
}
}
@media (max-width: 1200px) {
.login-fg .info h1 {
font-size: 45px;
}
}
@media (max-width: 992px) {
.login-fg .bg {
display: none;
}
}
@media (max-width: 768px) {
input,
.submit-btn {
padding: 12px;
font-size: 15px;
}
.login-fg .login-section {
padding: 15px;
max-width: 100%;
}
.login-fg .logo img {
max-width: 200px;
height: auto;
}
.login-fg .info h1 {
font-size: 32px;
margin-bottom: 10px;
}
.login-fg .info p {
font-size: 14px;
line-height: 1.5;
}
footer {
position: fixed;
margin-top: 20px;
padding: 8px;
font-size: 12px;
}
.login-fg .login-section h4 {
font-size: 18px;
margin-bottom: 20px;
}
.login-fg .login-section .social li a {
width: 100%;
margin: 5px 0;
}
.checkbox a {
display: block;
text-align: right;
margin-top: 10px;
}
.login-fg .login-section .social li a {
width: 100px;
}
.login-fg .logo a {
font-size: 26px;
}
}
@media (max-width: 480px) {
.login-fg .login {
padding: 15px;
}
input,
.submit-btn {
padding: 10px;
font-size: 15px;
}
.login-fg .login-section h4 {
font-size: 16px;
}
footer {
font-size: 11px;
}
}
@media (max-width: 320px) {
input,
.submit-btn {
padding: 8px;
font-size: 12px;
}
}
</style>
</head>
<body>
<!-- SweetAlert Notification -->
{if isset($notify)}
<script>
document.body.style.overflow = 'hidden';
Swal.fire({
icon: '{if $notify_t == "s"}success{else}warning{/if}',
title: '{if $notify_t == "s"}Success{else}Error{/if}',
html: '{$notify}',
backdrop: 'rgba(0, 0, 0, 0.5)',
}).then(() => {
document.body.style.overflow = '';
});
</script>
{/if}
<div class="login-fg">
<div class="container-fluid">
<div class="row">
<div class="col-xl-8 col-lg-7 col-md-12 bg"
style="background-image:url('./{$wallpaper}'); background-attachment: fixed;">
<div class="info">
<h1>{$_c['login_page_head']}</h1>
<p>{$_c['login_page_description']}</p>
</div>
</div>
<div class="col-xl-4 col-lg-5 col-md-12 login">
<div class="login-section">
<div class="logo clearfix">
<a href="./{$login_logo}" target="_blank"><img src="./{$login_logo}" height="60"
alt="Logo"></a>
</div>
<br>
<h4>{Lang::T('Create your account')}</h4>
<div class="form-container">
<form id="register-form" method="POST" action="{Text::url('register/post')}">
<input type="hidden" name="csrf_token" value="{$csrf_token}">
<!-- Basic Information (Initially Visible) -->
<div id="basicFields">
<div class="form-group">
<input type="text" name="username"
placeholder="{if $_c['country_code_phone']!= '' || $_c['registration_username'] == 'phone'}{$_c['country_code_phone']} {Lang::T('Phone Number')}{elseif $_c['registration_username'] == 'email'}{Lang::T('Email')}{else}{Lang::T('Usernames')}{/if}">
</div>
{if $_c['photo_register'] == 'yes'}
<div class="form-group">
<input type="file" required id="photo" name="photo"
accept="image/*">
</div>
{/if}
<div class="form-group">
<input type="text" name="fullname" placeholder="{Lang::T('Full Name')}"
{if $_c['man_fields_fname'] neq 'no'}required{/if} >
</div>
<div class="form-group">
<input type="email" name="email" placeholder="{Lang::T('Email Address')}"
{if $_c['man_fields_email'] neq 'no'}required{/if}>
</div>
<div class="form-group">
<button type="button" onclick="toggleFields()" class="submit-btn">
{Lang::T('Next Step')}
</button>
</div>
</div>
<!-- Password Fields (Initially Hidden) -->
<div id="passwordFields" style="display: none;">
<div class="form-group">
<input type="text" name="address" placeholder="{Lang::T('Home Address')}"
{if $_c['man_fields_address'] neq 'no'}required{/if}>
</div>
<div class="form-group">
<input type="password" name="password" placeholder="{Lang::T('Password')}"
required>
</div>
<div class="form-group">
<input type="password" name="cpassword"
placeholder="{Lang::T('Confirm Password')}" required>
</div>
<div class="form-group">
<button id="register-btn" type="submit" class="submit-btn">
<span id="register-text">{Lang::T('Register')}</span>
</button>
</div>
</div>
</form>
</div>
<p>{Lang::T('Already have an account?')} <a href="{Text::url('login')}"
class="linkButton">{Lang::T('Login')}</a></p>
<footer>
© {$smarty.now|date_format:"%Y"} {$_c['CompanyName']}. All rights reserved. <br> <a
href="pages/Privacy_Policy.html">Privacy</a> | <a
href="pages/Terms_and_Conditions.html">Terms &amp; Conditions</a>
</footer>
</div>
</div>
</div>
</div>
</div>
<script>
const registerForm = document.getElementById('register-form');
const registerBtn = document.getElementById('register-btn');
const registerText = document.getElementById('register-text');
registerForm.addEventListener('submit', function (event) {
registerBtn.classList.add('loading');
registerText.textContent = 'Please Wait...';
});
function toggleFields() {
document.getElementById('basicFields').style.display = 'none';
document.getElementById('passwordFields').style.display = 'block';
const backButton = document.createElement('button');
backButton.className = 'submit-btn';
backButton.textContent = '← Back';
backButton.style.marginBottom = '15px';
backButton.onclick = () => {
document.getElementById('basicFields').style.display = 'block';
document.getElementById('passwordFields').style.display = 'none';
backButton.remove();
};
document.getElementById('passwordFields').prepend(backButton);
}
</script>
</body>
</html>

View File

@ -4,7 +4,20 @@
<table class="table table-condensed">
<thead>
<tr>
<th>{Lang::T('Username')}</th>
<th>
<select style="border: 0px; width: 100%; background-color: #f9f9f9;"
onchange="changeExpiredDefault(this)">
<option value="username" {if $cookie['expdef'] == 'username'}selected{/if}>
{Lang::T('Username')}
</option>
<option value="fullname" {if $cookie['expdef'] == 'fullname'}selected{/if}>
{Lang::T('Full Name')}</option>
<option value="phone" {if $cookie['expdef'] == 'phone'}selected{/if}>{Lang::T('Phone')}
</option>
<option value="email" {if $cookie['expdef'] == 'email'}selected{/if}>{Lang::T('Email')}
</option>
</select>
</th>
<th>{Lang::T('Created / Expired')}</th>
<th>{Lang::T('Internet Package')}</th>
<th>{Lang::T('Location')}</th>
@ -15,7 +28,17 @@
{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><a href="{Text::url('customers/view/',$expired['id'])}">
{if $cookie['expdef'] == 'fullname'}
{$expired['fullname']}
{elseif $cookie['expdef'] == 'phone'}
{$expired['phonenumber']}
{elseif $cookie['expdef'] == 'email'}
{$expired['email']}
{else}
{$expired['username']}
{/if}
</a></td>
<td><small data-toggle="tooltip" data-placement="top"
title="{Lang::dateAndTimeFormat($expired['recharged_on'],$expired['recharged_time'])}">{Lang::timeElapsed($rem_started)}</small>
/
@ -31,3 +54,11 @@
</div>
&nbsp; {include file="pagination.tpl"}
</div>
<script>
function changeExpiredDefault(fl) {
setCookie('expdef', fl.value, 365);
setTimeout(() => {
location.reload();
}, 1000);
}
</script>

View File

@ -1,3 +1,3 @@
{
"version": "2025.3.10"
"version": "2025.3.13"
}