Merge pull request 'Development' (#2) from Development into master
Reviewed-on: #2
This commit is contained in:
commit
915af6fc03
2
.gitignore
vendored
2
.gitignore
vendored
@ -58,3 +58,5 @@ docs/**
|
||||
!docs/insomnia.rest.json
|
||||
!system/uploads/paid.png
|
||||
system/uploads/invoices/**
|
||||
!system/uploads/invoices/
|
||||
!system/uploads/invoices/index.html
|
@ -8,12 +8,12 @@ class Invoice
|
||||
{
|
||||
try {
|
||||
if (empty($invoiceData['invoice'])) {
|
||||
throw new Exception("Invoice ID is required");
|
||||
throw new Exception(Lang::T("Invoice No is required"));
|
||||
}
|
||||
|
||||
$template = Lang::getNotifText('email_invoice');
|
||||
if (!$template) {
|
||||
throw new Exception("Invoice template not found");
|
||||
throw new Exception(Lang::T("Invoice template not found"));
|
||||
}
|
||||
|
||||
if (strpos($template, '<body') === false) {
|
||||
@ -47,10 +47,14 @@ class Invoice
|
||||
// Save PDF
|
||||
$filename = "invoice_{$invoiceData['invoice']}.pdf";
|
||||
$outputPath = "system/uploads/invoices/{$filename}";
|
||||
$dir = dirname($outputPath);
|
||||
if (!is_dir($dir)) {
|
||||
mkdir($dir, 0755, true);
|
||||
}
|
||||
$mpdf->Output($outputPath, 'F');
|
||||
|
||||
if (!file_exists($outputPath)) {
|
||||
throw new Exception("Failed to save PDF file");
|
||||
throw new Exception(Lang::T("Failed to save PDF file"));
|
||||
}
|
||||
|
||||
return $filename;
|
||||
@ -67,7 +71,7 @@ class Invoice
|
||||
return preg_replace_callback('/\[\[(\w+)\]\]/', function ($matches) use ($invoiceData) {
|
||||
$key = $matches[1];
|
||||
if (!isset($invoiceData[$key])) {
|
||||
_log("Missing invoice key: $key");
|
||||
_log(Lang::T("Missing invoice key: ") . $key);
|
||||
return '';
|
||||
}
|
||||
|
||||
@ -80,92 +84,84 @@ class Invoice
|
||||
}
|
||||
|
||||
if ($key === 'bill_rows') {
|
||||
return html_entity_decode($invoiceData[$key]);
|
||||
return $invoiceData[$key];
|
||||
}
|
||||
|
||||
|
||||
return htmlspecialchars($invoiceData[$key] ?? '');
|
||||
}, $template);
|
||||
}
|
||||
|
||||
public static function sendInvoice($userId, $status = "Unpaid")
|
||||
/**
|
||||
* Send invoice to user
|
||||
*
|
||||
* @param int $userId
|
||||
* @param array $invoice
|
||||
* @param array $bills
|
||||
* @param string $status
|
||||
* @param string $invoiceNo
|
||||
* @return bool
|
||||
*/
|
||||
|
||||
public static function sendInvoice($userId, $invoice = null, $bills = [], $status = "Unpaid", $invoiceNo = null)
|
||||
{
|
||||
global $config, $root_path, $UPLOAD_PATH;
|
||||
|
||||
if (empty($config['currency_code'])) {
|
||||
$config['currency_code'] = '$';
|
||||
}
|
||||
// Set default currency code
|
||||
$config['currency_code'] ??= '$';
|
||||
|
||||
$account = ORM::for_table('tbl_customers')->find_one($userId);
|
||||
|
||||
if (!$account) {
|
||||
_log("Failed to send invoice: User not found");
|
||||
sendTelegram("Failed to send invoice: User not found");
|
||||
return false;
|
||||
self::validateAccount($account);
|
||||
if (!$invoiceNo) {
|
||||
$invoiceNo = "INV-" . Package::_raid();
|
||||
}
|
||||
// Fetch invoice if not provided
|
||||
if ($status === "Unpaid" && !$invoice) {
|
||||
$data = ORM::for_table('tbl_user_recharges')->where('customer_id', $userId)
|
||||
->where('status', 'off')
|
||||
->left_outer_join('tbl_plans', 'tbl_user_recharges.namebp = tbl_plans.name_plan')
|
||||
->select('tbl_plans.price', 'price')
|
||||
->select('tbl_plans.name_plan', 'namebp')
|
||||
->find_one();
|
||||
if (!$data) {
|
||||
$data = ORM::for_table('tbl_user_recharges')->where('username', $account->username)
|
||||
->left_outer_join('tbl_plans', 'tbl_user_recharges.namebp = tbl_plans.name_plan')
|
||||
->select('tbl_plans.price', 'price')
|
||||
->select('tbl_plans.name_plan', 'namebp')
|
||||
->where('status', 'off')
|
||||
->find_one();
|
||||
}
|
||||
if (!$data) {
|
||||
throw new Exception(Lang::T("No unpaid invoice found for username:") . $account->username);
|
||||
}
|
||||
$invoice = [
|
||||
'price' => $data->price,
|
||||
'plan_name' => $data->namebp,
|
||||
'routers' => $data->routers,
|
||||
];
|
||||
|
||||
} else if ($status === "Paid" && !$invoice) {
|
||||
$invoice = ORM::for_table("tbl_transactions")->where("username", $account->username)->find_one();
|
||||
|
||||
}
|
||||
if (!$invoice) {
|
||||
_log("Failed to send invoice: Transaction not found");
|
||||
sendTelegram("Failed to send invoice: Transaction not found");
|
||||
return false;
|
||||
throw new Exception(Lang::T("Transaction not found for username: ") . $account->username);
|
||||
}
|
||||
|
||||
[$additionalBills, $add_cost] = User::getBills($account->id);
|
||||
|
||||
$invoiceItems = [
|
||||
[
|
||||
'description' => $invoice->plan_name,
|
||||
'details' => 'Monthly Subscription',
|
||||
'amount' => (float) $invoice->price
|
||||
]
|
||||
];
|
||||
$subtotal = (float) $invoice->price;
|
||||
|
||||
if ($add_cost > 0 && $invoice->routers != 'balance') {
|
||||
foreach ($additionalBills as $description => $amount) {
|
||||
if (is_numeric($amount)) {
|
||||
$invoiceItems[] = [
|
||||
'description' => $description,
|
||||
'details' => 'Additional Bill',
|
||||
'amount' => (float) $amount
|
||||
];
|
||||
$subtotal += (float) $amount;
|
||||
} else {
|
||||
_log("Invalid bill amount for {$description}: {$amount}");
|
||||
}
|
||||
}
|
||||
// Get additional bills if not provided
|
||||
if (empty($bills)) {
|
||||
[$bills, $add_cost] = User::getBills($account->id);
|
||||
}
|
||||
|
||||
$tax_rate = (float) ($config['tax_rate'] ?? 0);
|
||||
$invoiceItems = self::generateInvoiceItems($invoice, $bills, $add_cost);
|
||||
$subtotal = array_sum(array_column($invoiceItems, 'amount'));
|
||||
$tax = $config['enable_tax'] ? Package::tax($subtotal) : 0;
|
||||
$total = ($tax > 0) ? $subtotal + $tax : $subtotal + $tax;
|
||||
$tax_rate = $config['tax_rate'] ?? 0;
|
||||
$total = $subtotal + $tax;
|
||||
|
||||
$token = User::generateToken($account->id, 1);
|
||||
if (!empty($token['token'])) {
|
||||
$tur = ORM::for_table('tbl_user_recharges')
|
||||
->where('customer_id', $account->id)
|
||||
->where('namebp', $invoice->plan_name);
|
||||
$payLink = self::generatePaymentLink($account, $invoice, $status);
|
||||
$logo = self::getCompanyLogo($UPLOAD_PATH, $root_path);
|
||||
|
||||
switch ($status) {
|
||||
case 'Paid':
|
||||
$tur->where('status', 'on');
|
||||
break;
|
||||
default:
|
||||
$tur->where('status', 'off');
|
||||
break;
|
||||
}
|
||||
$turResult = $tur->find_one();
|
||||
$payLink = $turResult ? '?_route=home&recharge=' . $turResult['id'] . '&uid=' . urlencode($token['token']) : '?_route=home';
|
||||
} else {
|
||||
$payLink = '?_route=home';
|
||||
}
|
||||
|
||||
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
|
||||
$logo = (file_exists($UPLOAD_PATH . DIRECTORY_SEPARATOR . 'logo.png')) ? $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'logo.png?' . time() : $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'logo.default.png';
|
||||
$invoiceData = [
|
||||
'invoice' => "INV-" . Package::_raid(),
|
||||
'invoice' => $invoiceNo,
|
||||
'fullname' => $account->fullname,
|
||||
'email' => $account->email,
|
||||
'address' => $account->address,
|
||||
@ -182,31 +178,90 @@ class Invoice
|
||||
'payment_link' => $payLink
|
||||
];
|
||||
|
||||
if (!isset($invoiceData['bill_rows']) || empty($invoiceData['bill_rows'])) {
|
||||
_log("Invoice Error: Bill rows data is empty.");
|
||||
if (empty($invoiceData['bill_rows'])) {
|
||||
throw new Exception(Lang::T("Bill rows data is empty."));
|
||||
}
|
||||
|
||||
$filename = self::generateInvoice($invoiceData);
|
||||
if (!$filename) {
|
||||
throw new Exception(Lang::T("Failed to generate invoice PDF"));
|
||||
}
|
||||
|
||||
if ($filename) {
|
||||
$pdfPath = "system/uploads/invoices/{$filename}";
|
||||
self::saveToDatabase($filename, $account->id, $invoiceData, $total);
|
||||
|
||||
try {
|
||||
Message::sendEmail(
|
||||
$account->email,
|
||||
"Invoice for Account {$account->fullname}",
|
||||
"Please find your invoice attached",
|
||||
Lang::T("Invoice for Account {$account->fullname}"),
|
||||
Lang::T("Please find your invoice attached"),
|
||||
$pdfPath
|
||||
);
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
_log("Failed to send invoice email: " . $e->getMessage());
|
||||
sendTelegram("Failed to send invoice email: " . $e->getMessage());
|
||||
return false;
|
||||
throw new Exception(Lang::T("Failed to send email invoice to ") . $account->email . ". " . Lang::T("Reason: ") . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
private static function validateAccount($account)
|
||||
{
|
||||
if (!$account) {
|
||||
throw new Exception(Lang::T("User not found"));
|
||||
}
|
||||
if (!$account->email || !filter_var($account->email, FILTER_VALIDATE_EMAIL)) {
|
||||
throw new Exception(Lang::T("Invalid user email"));
|
||||
}
|
||||
}
|
||||
|
||||
private static function generateInvoiceItems($invoice, $bills, $add_cost)
|
||||
{
|
||||
$items = [
|
||||
[
|
||||
'description' => $invoice['plan_name'],
|
||||
'details' => Lang::T('Subscription'),
|
||||
'amount' => (float) $invoice['price']
|
||||
]
|
||||
];
|
||||
|
||||
if ($invoice->routers != 'balance') {
|
||||
foreach ($bills as $description => $amount) {
|
||||
if (is_numeric($amount)) {
|
||||
$items[] = [
|
||||
'description' => $description,
|
||||
'details' => Lang::T('Additional Bill'),
|
||||
'amount' => (float) $amount
|
||||
];
|
||||
} else {
|
||||
_log(Lang::T("Invalid bill amount for {$description}: {$amount}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
private static function generatePaymentLink($account, $invoice, $status)
|
||||
{
|
||||
$token = User::generateToken($account->id, 1);
|
||||
if (empty($token['token'])) {
|
||||
return '?_route=home';
|
||||
}
|
||||
|
||||
$tur = ORM::for_table('tbl_user_recharges')
|
||||
->where('customer_id', $account->id)
|
||||
->where('namebp', $invoice->plan_name);
|
||||
|
||||
$tur->where('status', $status === 'Paid' ? 'on' : 'off');
|
||||
$turResult = $tur->find_one();
|
||||
|
||||
return $turResult ? '?_route=home&recharge=' . $turResult['id'] . '&uid=' . urlencode($token['token']) : '?_route=home';
|
||||
}
|
||||
|
||||
private static function getCompanyLogo($UPLOAD_PATH, $root_path)
|
||||
{
|
||||
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
|
||||
return file_exists($UPLOAD_PATH . DIRECTORY_SEPARATOR . 'logo.png') ?
|
||||
$UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'logo.png?' . time() :
|
||||
$UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'logo.default.png';
|
||||
}
|
||||
|
||||
private static function generateBillRows($items, $currency, $subtotal, $tax_rate, $tax, $total)
|
||||
@ -222,9 +277,11 @@ class Invoice
|
||||
<tbody>";
|
||||
|
||||
foreach ($items as $item) {
|
||||
$desc = htmlspecialchars($item['description'], ENT_QUOTES);
|
||||
$details = htmlspecialchars($item['details'], ENT_QUOTES);
|
||||
$html .= "<tr>
|
||||
<td style='padding: 10px; border-bottom: 1px solid #ddd;'>{$item['description']}</td>
|
||||
<td style='padding: 10px; border-bottom: 1px solid #ddd;'>{$item['details']}</td>
|
||||
<td style='padding: 10px; border-bottom: 1px solid #ddd;'>{$desc}</td>
|
||||
<td style='padding: 10px; border-bottom: 1px solid #ddd;'>{$details}</td>
|
||||
<td style='padding: 10px; border-bottom: 1px solid #ddd;'>{$currency}" . number_format((float) $item['amount'], 2) . "</td>
|
||||
</tr>";
|
||||
}
|
||||
@ -247,6 +304,44 @@ class Invoice
|
||||
return $html;
|
||||
}
|
||||
|
||||
private static function saveToDatabase($filename, $customer_id, $invoiceData, $total)
|
||||
{
|
||||
$invoice = ORM::for_table('tbl_invoices')->create();
|
||||
$invoice->number = $invoiceData['invoice'];
|
||||
$invoice->customer_id = $customer_id;
|
||||
$invoice->fullname = $invoiceData['fullname'];
|
||||
$invoice->email = $invoiceData['email'];
|
||||
$invoice->address = $invoiceData['address'];
|
||||
$invoice->status = $invoiceData['status'];
|
||||
$invoice->due_date = $invoiceData['due_date'];
|
||||
$invoice->filename = $filename;
|
||||
$invoice->amount = $total;
|
||||
$invoice->data = json_encode($invoiceData);
|
||||
$invoice->created_at = date('Y-m-d H:i:s');
|
||||
$invoice->save();
|
||||
return $invoice->id;
|
||||
}
|
||||
|
||||
public static function getAll()
|
||||
{
|
||||
return ORM::for_table('tbl_invoices')->order_by_desc('id')->find_many();
|
||||
}
|
||||
public static function getById($id)
|
||||
{
|
||||
return ORM::for_table('tbl_invoices')->find_one($id);
|
||||
}
|
||||
public static function getByNumber($number)
|
||||
{
|
||||
return ORM::for_table('tbl_invoices')->where('number', $number)->find_one();
|
||||
}
|
||||
public static function delete($id)
|
||||
{
|
||||
$invoice = ORM::for_table('tbl_invoices')->find_one($id);
|
||||
if ($invoice) {
|
||||
$invoice->delete();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ class Message
|
||||
$txts = str_split($txt, 160);
|
||||
try {
|
||||
foreach ($txts as $txt) {
|
||||
self::sendSMS($config['sms_url'], $phone, $txt);
|
||||
self::sendSMS($phone, $txt);
|
||||
self::logMessage('SMS', $phone, $txt, 'Success');
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
@ -140,6 +140,7 @@ class Message
|
||||
}
|
||||
mail($to, $subject, $body, $attr);
|
||||
self::logMessage('Email', $to, $body, 'Success');
|
||||
return true;
|
||||
} else {
|
||||
$mail = new PHPMailer();
|
||||
$mail->isSMTP();
|
||||
@ -188,8 +189,10 @@ class Message
|
||||
if (!$mail->send()) {
|
||||
$errorMessage = Lang::T("Email not sent, Mailer Error: ") . $mail->ErrorInfo;
|
||||
self::logMessage('Email', $to, $body, 'Error', $errorMessage);
|
||||
return false;
|
||||
} else {
|
||||
self::logMessage('Email', $to, $body, 'Success');
|
||||
return true;
|
||||
}
|
||||
|
||||
//<p style="font-family: Helvetica, sans-serif; font-size: 16px; font-weight: normal; margin: 0; margin-bottom: 16px;">
|
||||
@ -396,9 +399,11 @@ class Message
|
||||
$v->body = nl2br($body);
|
||||
$v->save();
|
||||
self::logMessage("Inbox", $user->username, $body, "Success");
|
||||
return true;
|
||||
} catch (Throwable $e) {
|
||||
$errorMessage = Lang::T("Error adding message to inbox: " . $e->getMessage());
|
||||
self::logMessage('Inbox', $user->username, $body, 'Error', $errorMessage);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,7 +114,10 @@ switch ($action) {
|
||||
$query = ORM::for_table('tbl_transactions')
|
||||
->whereRaw("UNIX_TIMESTAMP(CONCAT(`recharged_on`,' ',`recharged_time`)) >= " . strtotime("$sd $ts"))
|
||||
->whereRaw("UNIX_TIMESTAMP(CONCAT(`recharged_on`,' ',`recharged_time`)) <= " . strtotime("$ed $te"))
|
||||
->order_by_desc('id');
|
||||
->left_outer_join('tbl_customers', 'tbl_transactions.username = tbl_customers.username')
|
||||
->select('tbl_transactions.*')
|
||||
->select('tbl_customers.fullname', 'fullname')
|
||||
->order_by_desc('tbl_transactions.id');
|
||||
if (count($tps) > 0) {
|
||||
$query->where_in('type', $tps);
|
||||
}
|
||||
@ -154,10 +157,11 @@ switch ($action) {
|
||||
</div>
|
||||
<div id="logo"><img id="image" src="' . $logo . '" alt="logo" /></div>
|
||||
</div>
|
||||
<div id="header">' . Lang::T('All Transactions at Date') . ': ' . Lang::dateAndTimeFormat($sd, $ts) .' - '. Lang::dateAndTimeFormat($ed, $te) . '</div>
|
||||
<div id="header">' . Lang::T('All Transactions at Date') . ': ' . Lang::dateAndTimeFormat($sd, $ts) . ' - ' . Lang::dateAndTimeFormat($ed, $te) . '</div>
|
||||
<table id="customers">
|
||||
<tr>
|
||||
<th>' . Lang::T('Username') . '</th>
|
||||
<th>' . Lang::T('Fullname') . '</th>
|
||||
<th>' . Lang::T('Plan Name') . '</th>
|
||||
<th>' . Lang::T('Type') . '</th>
|
||||
<th>' . Lang::T('Plan Price') . '</th>
|
||||
@ -170,6 +174,7 @@ switch ($action) {
|
||||
foreach ($x as $value) {
|
||||
|
||||
$username = $value['username'];
|
||||
$fullname = $value['fullname'];
|
||||
$plan_name = $value['plan_name'];
|
||||
$type = $value['type'];
|
||||
$price = $config['currency_code'] . ' ' . number_format($value['price'], 0, $config['dec_point'], $config['thousands_sep']);
|
||||
@ -181,6 +186,7 @@ switch ($action) {
|
||||
|
||||
$html .= "<tr" . (($c = !$c) ? ' class="alt"' : ' class=""') . ">" . "
|
||||
<td>$username</td>
|
||||
<td>$fullname</td>
|
||||
<td>$plan_name</td>
|
||||
<td>$type</td>
|
||||
<td align='right'>$price</td>
|
||||
@ -245,7 +251,7 @@ $style
|
||||
$html
|
||||
EOF;
|
||||
$mpdf->WriteHTML($nhtml);
|
||||
$mpdf->Output('phpnuxbill_reports_'.date('Ymd_His') . '.pdf', 'D');
|
||||
$mpdf->Output('phpnuxbill_reports_' . date('Ymd_His') . '.pdf', 'D');
|
||||
} else {
|
||||
echo 'No Data';
|
||||
}
|
||||
@ -258,6 +264,10 @@ EOF;
|
||||
$stype = _post('stype');
|
||||
|
||||
$d = ORM::for_table('tbl_transactions');
|
||||
$d->left_outer_join('tbl_customers', 'tbl_transactions.username = tbl_customers.username')
|
||||
->select('tbl_transactions.*')
|
||||
->select('tbl_customers.fullname', 'fullname')
|
||||
->order_by_desc('tbl_transactions.id');
|
||||
if ($stype != '') {
|
||||
$d->where('type', $stype);
|
||||
}
|
||||
@ -290,6 +300,10 @@ EOF;
|
||||
$tdate = _post('tdate');
|
||||
$stype = _post('stype');
|
||||
$d = ORM::for_table('tbl_transactions');
|
||||
$d->left_outer_join('tbl_customers', 'tbl_transactions.username = tbl_customers.username')
|
||||
->select('tbl_transactions.*')
|
||||
->select('tbl_customers.fullname', 'fullname')
|
||||
->order_by_desc('tbl_transactions.id');
|
||||
if ($stype != '') {
|
||||
$d->where('type', $stype);
|
||||
}
|
||||
@ -332,6 +346,7 @@ EOF;
|
||||
<table id="customers">
|
||||
<tr>
|
||||
<th>' . Lang::T('Username') . '</th>
|
||||
<th>' . Lang::T('Fullname') . '</th>
|
||||
<th>' . Lang::T('Plan Name') . '</th>
|
||||
<th>' . Lang::T('Type') . '</th>
|
||||
<th>' . Lang::T('Plan Price') . '</th>
|
||||
@ -344,6 +359,7 @@ EOF;
|
||||
foreach ($x as $value) {
|
||||
|
||||
$username = $value['username'];
|
||||
$fullname = $value['fullname'];
|
||||
$plan_name = $value['plan_name'];
|
||||
$type = $value['type'];
|
||||
$price = $config['currency_code'] . ' ' . number_format($value['price'], 0, $config['dec_point'], $config['thousands_sep']);
|
||||
@ -355,6 +371,7 @@ EOF;
|
||||
|
||||
$html .= "<tr" . (($c = !$c) ? ' class="alt"' : ' class=""') . ">" . "
|
||||
<td>$username</td>
|
||||
<td>$fullname</td>
|
||||
<td>$plan_name</td>
|
||||
<td>$type</td>
|
||||
<td align='right'>$price</td>
|
||||
|
27
system/controllers/invoices.php
Normal file
27
system/controllers/invoices.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
*
|
||||
**/
|
||||
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', Lang::T('Invoice Lists'));
|
||||
$ui->assign('_system_menu', 'reports');
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
if (empty($action)) {
|
||||
$action = 'list';
|
||||
}
|
||||
switch ($action) {
|
||||
case 'list':
|
||||
$ui->assign('xheader', '<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.11.3/css/jquery.dataTables.min.css">');
|
||||
$ui->assign('invoices', Invoice::getAll());
|
||||
$ui->display('admin/invoices/list.tpl');
|
||||
break;
|
||||
default:
|
||||
$ui->display('admin/404.tpl');
|
||||
}
|
@ -57,56 +57,79 @@ EOT;
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
|
||||
// Get form data
|
||||
$id_customer = $_POST['id_customer'];
|
||||
$message = $_POST['message'];
|
||||
$via = $_POST['via'];
|
||||
$id_customer = $_POST['id_customer'] ?? '';
|
||||
$message = $_POST['message'] ?? '';
|
||||
$via = $_POST['via'] ?? '';
|
||||
$subject = $_POST['subject'] ?? '';
|
||||
|
||||
// Check if fields are empty
|
||||
if ($id_customer == '' or $message == '' or $via == '') {
|
||||
r2(getUrl('message/send'), 'e', Lang::T('All field is required'));
|
||||
} else {
|
||||
// Get customer details from the database
|
||||
$c = ORM::for_table('tbl_customers')->find_one($id_customer);
|
||||
// Validate subject based on the selected channel
|
||||
if (($via === 'all' || $via === 'email' || $via === 'inbox') && empty($subject)) {
|
||||
r2(getUrl('message/send'), 'e', LANG::T('Subject is required to send message using') . ' ' . $via . '.');
|
||||
}
|
||||
|
||||
if (empty($id_customer) || empty($message) || empty($via)) {
|
||||
r2(getUrl('message/send'), 'e', Lang::T('Customer, Message, and Channel are required'));
|
||||
}
|
||||
|
||||
$customer = ORM::for_table('tbl_customers')->find_one($id_customer);
|
||||
if (!$customer) {
|
||||
r2(getUrl('message/send'), 'e', Lang::T('Customer not found'));
|
||||
}
|
||||
|
||||
// Replace placeholders in message and subject
|
||||
$currentMessage = str_replace(
|
||||
['[[name]]', '[[user_name]]', '[[phone]]', '[[company_name]]'],
|
||||
[$customer['fullname'], $customer['username'], $customer['phonenumber'], $config['CompanyName']],
|
||||
$message
|
||||
);
|
||||
|
||||
$currentSubject = str_replace(
|
||||
['[[name]]', '[[user_name]]', '[[phone]]', '[[company_name]]'],
|
||||
[$customer['fullname'], $customer['username'], $customer['phonenumber'], $config['CompanyName']],
|
||||
$subject
|
||||
);
|
||||
|
||||
// Replace placeholders in the message with actual values
|
||||
$message = str_replace('[[name]]', $c['fullname'], $message);
|
||||
$message = str_replace('[[user_name]]', $c['username'], $message);
|
||||
$message = str_replace('[[phone]]', $c['phonenumber'], $message);
|
||||
$message = str_replace('[[company_name]]', $config['CompanyName'], $message);
|
||||
if (strpos($message, '[[payment_link]]') !== false) {
|
||||
// token only valid for 1 day, for security reason
|
||||
$token = User::generateToken($c['id'], 1);
|
||||
$token = User::generateToken($customer['id'], 1);
|
||||
if (!empty($token['token'])) {
|
||||
$tur = ORM::for_table('tbl_user_recharges')
|
||||
->where('customer_id', $c['id'])
|
||||
//->where('namebp', $package)
|
||||
->where('customer_id', $customer['id'])
|
||||
->find_one();
|
||||
if ($tur) {
|
||||
$url = '?_route=home&recharge=' . $tur['id'] . '&uid=' . urlencode($token['token']);
|
||||
$message = str_replace('[[payment_link]]', $url, $message);
|
||||
$currentMessage = str_replace('[[payment_link]]', $url, $currentMessage);
|
||||
}
|
||||
} else {
|
||||
$message = str_replace('[[payment_link]]', '', $message);
|
||||
$currentMessage = str_replace('[[payment_link]]', '', $currentMessage);
|
||||
}
|
||||
}
|
||||
|
||||
// Send the message through the selected channels
|
||||
$smsSent = $waSent = $emailSent = $inboxSent = false;
|
||||
|
||||
//Send the message
|
||||
if ($via == 'sms' || $via == 'both') {
|
||||
$smsSent = Message::sendSMS($c['phonenumber'], $message);
|
||||
if ($via === 'sms' || $via === 'both' || $via === 'all') {
|
||||
$smsSent = Message::sendSMS($customer['phonenumber'], $currentSubject);
|
||||
}
|
||||
|
||||
if ($via == 'wa' || $via == 'both') {
|
||||
$waSent = Message::sendWhatsapp($c['phonenumber'], $message);
|
||||
if ($via === 'wa' || $via === 'both' || $via === 'all') {
|
||||
$waSent = Message::sendWhatsapp($customer['phonenumber'], $currentSubject);
|
||||
}
|
||||
|
||||
if (isset($smsSent) || isset($waSent)) {
|
||||
if ($via === 'email' || $via === 'all') {
|
||||
$emailSent = Message::sendEmail($customer['email'], $currentSubject, $currentMessage);
|
||||
}
|
||||
|
||||
if ($via === 'inbox' || $via === 'all') {
|
||||
$inboxSent = Message::addToInbox($customer['id'], $currentSubject, $currentMessage, 'Admin');
|
||||
}
|
||||
|
||||
// Check if any message was sent successfully
|
||||
if ($smsSent || $waSent || $emailSent || $inboxSent) {
|
||||
r2(getUrl('message/send'), 's', Lang::T('Message Sent Successfully'));
|
||||
} else {
|
||||
r2(getUrl('message/send'), 'e', Lang::T('Failed to send message'));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'send_bulk':
|
||||
@ -133,11 +156,16 @@ EOT;
|
||||
$batch = $_REQUEST['batch'] ?? 100;
|
||||
$page = $_REQUEST['page'] ?? 0;
|
||||
$router = $_REQUEST['router'] ?? null;
|
||||
$test = isset($_REQUEST['test']) && $_REQUEST['test'] === 'on' ? true : false;
|
||||
$test = isset($_REQUEST['test']) && $_REQUEST['test'] === 'on';
|
||||
$service = $_REQUEST['service'] ?? '';
|
||||
$subject = $_REQUEST['subject'] ?? '';
|
||||
|
||||
if (empty($group) || empty($message) || empty($via) || empty($service)) {
|
||||
die(json_encode(['status' => 'error', 'message' => 'All fields are required']));
|
||||
die(json_encode(['status' => 'error', 'message' => LANG::T('All fields are required')]));
|
||||
}
|
||||
|
||||
if (in_array($via, ['all', 'email', 'inbox']) && empty($subject)) {
|
||||
die(json_encode(['status' => 'error', 'message' => LANG::T('Subject is required to send message using') . ' ' . $via . '.']));
|
||||
}
|
||||
|
||||
// Get batch of customers based on group
|
||||
@ -153,7 +181,7 @@ EOT;
|
||||
default:
|
||||
$router = ORM::for_table('tbl_routers')->find_one($router);
|
||||
if (!$router) {
|
||||
die(json_encode(['status' => 'error', 'message' => 'Invalid router']));
|
||||
die(json_encode(['status' => 'error', 'message' => LANG::T('Invalid router')]));
|
||||
}
|
||||
$routerName = $router->name;
|
||||
break;
|
||||
@ -200,6 +228,9 @@ EOT;
|
||||
['tbl_customers.phonenumber', 'phonenumber'],
|
||||
['tbl_user_recharges.customer_id', 'customer_id'],
|
||||
['tbl_customers.fullname', 'fullname'],
|
||||
['tbl_customers.username', 'username'],
|
||||
['tbl_customers.email', 'email'],
|
||||
['tbl_customers.service_type', 'service_type'],
|
||||
]);
|
||||
$customers = $query->find_array();
|
||||
} else {
|
||||
@ -287,7 +318,13 @@ EOT;
|
||||
$totalSMSFailed = 0;
|
||||
$totalWhatsappSent = 0;
|
||||
$totalWhatsappFailed = 0;
|
||||
$totalEmailSent = 0;
|
||||
$totalEmailFailed = 0;
|
||||
$totalInboxSent = 0;
|
||||
$totalInboxFailed = 0;
|
||||
$batchStatus = [];
|
||||
//$subject = $config['CompanyName'] . ' ' . Lang::T('Notification Message');
|
||||
$form = 'Admin';
|
||||
|
||||
foreach ($customers as $customer) {
|
||||
$currentMessage = str_replace(
|
||||
@ -296,6 +333,12 @@ EOT;
|
||||
$message
|
||||
);
|
||||
|
||||
$currentSubject = str_replace(
|
||||
['[[name]]', '[[user_name]]', '[[phone]]', '[[company_name]]'],
|
||||
[$customer['fullname'], $customer['username'], $customer['phonenumber'], $config['CompanyName']],
|
||||
$subject
|
||||
);
|
||||
|
||||
$phoneNumber = preg_replace('/\D/', '', $customer['phonenumber']);
|
||||
|
||||
if (empty($phoneNumber)) {
|
||||
@ -310,14 +353,14 @@ EOT;
|
||||
if ($test) {
|
||||
$batchStatus[] = [
|
||||
'name' => $customer['fullname'],
|
||||
'phone' => $customer['phonenumber'],
|
||||
'channel' => 'Test Channel',
|
||||
'status' => 'Test Mode',
|
||||
'message' => $currentMessage,
|
||||
'service' => $service,
|
||||
'router' => $routerName,
|
||||
];
|
||||
} else {
|
||||
if ($via == 'sms' || $via == 'both') {
|
||||
if ($via === 'sms' || $via === 'both' || $via === 'all') {
|
||||
if (Message::sendSMS($customer['phonenumber'], $currentMessage)) {
|
||||
$totalSMSSent++;
|
||||
$batchStatus[] = ['name' => $customer['fullname'], 'phone' => $customer['phonenumber'], 'status' => 'SMS Sent', 'message' => $currentMessage];
|
||||
@ -327,13 +370,33 @@ EOT;
|
||||
}
|
||||
}
|
||||
|
||||
if ($via == 'wa' || $via == 'both') {
|
||||
if ($via === 'wa' || $via == 'both' || $via === 'all') {
|
||||
if (Message::sendWhatsapp($customer['phonenumber'], $currentMessage)) {
|
||||
$totalWhatsappSent++;
|
||||
$batchStatus[] = ['name' => $customer['fullname'], 'phone' => $customer['phonenumber'], 'status' => 'WhatsApp Sent', 'message' => $currentMessage];
|
||||
$batchStatus[] = ['name' => $customer['fullname'], 'channel' => $customer['phonenumber'], 'status' => 'WhatsApp Sent', 'message' => $currentMessage];
|
||||
} else {
|
||||
$totalWhatsappFailed++;
|
||||
$batchStatus[] = ['name' => $customer['fullname'], 'phone' => $customer['phonenumber'], 'status' => 'WhatsApp Failed', 'message' => $currentMessage];
|
||||
$batchStatus[] = ['name' => $customer['fullname'], 'channel' => $customer['phonenumber'], 'status' => 'WhatsApp Failed', 'message' => $currentMessage];
|
||||
}
|
||||
}
|
||||
|
||||
if ($via === 'email' || $via === 'all') {
|
||||
if (Message::sendEmail($customer['email'], $currentSubject, $currentMessage)) {
|
||||
$totalEmailSent++;
|
||||
$batchStatus[] = ['name' => $customer['fullname'], 'channel' => $customer['email'], 'status' => 'Email Sent', 'message' => $currentMessage];
|
||||
} else {
|
||||
$totalEmailFailed++;
|
||||
$batchStatus[] = ['name' => $customer['fullname'], 'channel' => $customer['email'], 'status' => 'Email Failed', 'message' => $currentMessage];
|
||||
}
|
||||
}
|
||||
|
||||
if ($via === 'inbox' || $via === 'all') {
|
||||
if (Message::addToInbox($customer['customer_id'], $currentSubject, $currentMessage, $form)) {
|
||||
$totalInboxSent++;
|
||||
$batchStatus[] = ['name' => $customer['fullname'], 'channel' => 'Inbox', 'status' => 'Inbox Message Sent', 'message' => $currentMessage];
|
||||
} else {
|
||||
$totalInboxFailed++;
|
||||
$batchStatus[] = ['name' => $customer['fullname'], 'channel' => 'Inbox', 'status' => 'Inbox Message Failed', 'message' => $currentMessage];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -348,8 +411,8 @@ EOT;
|
||||
'page' => $page + 1,
|
||||
'batchStatus' => $batchStatus,
|
||||
'message' => $currentMessage,
|
||||
'totalSent' => $totalSMSSent + $totalWhatsappSent,
|
||||
'totalFailed' => $totalSMSFailed + $totalWhatsappFailed,
|
||||
'totalSent' => $totalSMSSent + $totalWhatsappSent + $totalEmailSent + $totalInboxSent,
|
||||
'totalFailed' => $totalSMSFailed + $totalWhatsappFailed + $totalEmailFailed + $totalInboxFailed,
|
||||
'hasMore' => $hasMore,
|
||||
'service' => $service,
|
||||
'router' => $routerName,
|
||||
@ -365,16 +428,20 @@ EOT;
|
||||
// Get the posted data
|
||||
$customerIds = $_POST['customer_ids'] ?? [];
|
||||
$via = $_POST['message_type'] ?? '';
|
||||
$subject = $_POST['subject'] ?? '';
|
||||
$message = isset($_POST['message']) ? trim($_POST['message']) : '';
|
||||
if (empty($customerIds) || empty($message) || empty($via)) {
|
||||
echo json_encode(['status' => 'error', 'message' => Lang::T('Invalid customer IDs, Message, or Message Type.')]);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($via === 'all' || $via === 'email' || $via === 'inbox' && empty($subject)) {
|
||||
die(json_encode(['status' => 'error', 'message' => LANG::T('Subject is required to send message using') . ' ' . $via . '.']));
|
||||
}
|
||||
|
||||
// Prepare to send messages
|
||||
$sentCount = 0;
|
||||
$failedCount = 0;
|
||||
$subject = Lang::T('Notification Message');
|
||||
$form = 'Admin';
|
||||
|
||||
foreach ($customerIds as $customerId) {
|
||||
|
@ -58,7 +58,7 @@ switch ($action) {
|
||||
$w = [];
|
||||
$v = [];
|
||||
foreach ($mts as $mt) {
|
||||
$w[] ='method';
|
||||
$w[] = 'method';
|
||||
$v[] = "$mt - %";
|
||||
}
|
||||
$query->where_likes($w, $v);
|
||||
@ -91,7 +91,7 @@ switch ($action) {
|
||||
$w = [];
|
||||
$v = [];
|
||||
foreach ($mts as $mt) {
|
||||
$w[] ='method';
|
||||
$w[] = 'method';
|
||||
$v[] = "$mt - %";
|
||||
}
|
||||
$query->where_likes($w, $v);
|
||||
@ -161,7 +161,7 @@ switch ($action) {
|
||||
$w = [];
|
||||
$v = [];
|
||||
foreach ($mts as $mt) {
|
||||
$w[] ='method';
|
||||
$w[] = 'method';
|
||||
$v[] = "$mt - %";
|
||||
}
|
||||
$query->where_likes($w, $v);
|
||||
@ -246,7 +246,7 @@ switch ($action) {
|
||||
$total += $v;
|
||||
$array['data'][] = $v;
|
||||
}
|
||||
if($total>0){
|
||||
if ($total > 0) {
|
||||
$result['datas'][] = $array;
|
||||
}
|
||||
}
|
||||
@ -258,18 +258,29 @@ switch ($action) {
|
||||
die();
|
||||
case 'by-date':
|
||||
case 'activation':
|
||||
$q = (_post('q') ? _post('q') : _get('q'));
|
||||
$q = trim(_post('q') ?: _get('q'));
|
||||
$keep = _post('keep');
|
||||
|
||||
if (!empty($keep)) {
|
||||
ORM::raw_execute("DELETE FROM tbl_transactions WHERE date < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL $keep DAY))");
|
||||
r2(getUrl('logs/list/'), 's', "Delete logs older than $keep days");
|
||||
ORM::raw_execute("DELETE FROM tbl_transactions WHERE date < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL ? DAY))", [$keep]);
|
||||
r2(getUrl('reports/activation/'), 's', "Deleted logs older than $keep days");
|
||||
}
|
||||
if ($q != '') {
|
||||
$query = ORM::for_table('tbl_transactions')->where_like('invoice', '%' . $q . '%')->order_by_desc('id');
|
||||
|
||||
$query = ORM::for_table('tbl_transactions')
|
||||
->left_outer_join('tbl_customers', 'tbl_transactions.username = tbl_customers.username')
|
||||
->select('tbl_transactions.*')
|
||||
->select('tbl_customers.fullname', 'fullname')
|
||||
->order_by_desc('tbl_transactions.id');
|
||||
|
||||
if ($q !== '') {
|
||||
$query->where_like('invoice', "%$q%");
|
||||
}
|
||||
|
||||
try {
|
||||
$d = Paginator::findMany($query, ['q' => $q]);
|
||||
} else {
|
||||
$query = ORM::for_table('tbl_transactions')->order_by_desc('id');
|
||||
$d = Paginator::findMany($query);
|
||||
} catch (Exception $e) {
|
||||
r2(getUrl('reports/activation/'), 'e', 'Database query failed: ' . $e->getMessage());
|
||||
$d = [];
|
||||
}
|
||||
|
||||
$ui->assign('activation', $d);
|
||||
@ -291,6 +302,10 @@ switch ($action) {
|
||||
$stype = _post('stype');
|
||||
|
||||
$d = ORM::for_table('tbl_transactions');
|
||||
$d->left_outer_join('tbl_customers', 'tbl_transactions.username = tbl_customers.username')
|
||||
->select('tbl_transactions.*')
|
||||
->select('tbl_customers.fullname', 'fullname')
|
||||
->order_by_desc('tbl_transactions.id');
|
||||
if ($stype != '') {
|
||||
$d->where('type', $stype);
|
||||
}
|
||||
@ -348,7 +363,10 @@ switch ($action) {
|
||||
$query = ORM::for_table('tbl_transactions')
|
||||
->whereRaw("UNIX_TIMESTAMP(CONCAT(`recharged_on`,' ',`recharged_time`)) >= " . strtotime("$sd $ts"))
|
||||
->whereRaw("UNIX_TIMESTAMP(CONCAT(`recharged_on`,' ',`recharged_time`)) <= " . strtotime("$ed $te"))
|
||||
->order_by_desc('id');
|
||||
->left_outer_join('tbl_customers', 'tbl_transactions.username = tbl_customers.username')
|
||||
->select('tbl_transactions.*')
|
||||
->select('tbl_customers.fullname', 'fullname')
|
||||
->order_by_desc('tbl_transactions.id');
|
||||
if (count($tps) > 0) {
|
||||
$query->where_in('type', $tps);
|
||||
}
|
||||
@ -356,7 +374,7 @@ switch ($action) {
|
||||
$w = [];
|
||||
$v = [];
|
||||
foreach ($mts as $mt) {
|
||||
$w[] ='method';
|
||||
$w[] = 'method';
|
||||
$v[] = "$mt - %";
|
||||
}
|
||||
$query->where_likes($w, $v);
|
||||
|
@ -146,7 +146,8 @@ switch ($action) {
|
||||
$r = ORM::for_table('tbl_routers')->find_many();
|
||||
$ui->assign('r', $r);
|
||||
if (function_exists("shell_exec")) {
|
||||
$php = trim(shell_exec('which php'));
|
||||
$which = stripos(php_uname('s'), "Win") === 0 ? 'where' : 'which';
|
||||
$php = trim(shell_exec("$which php"));
|
||||
if (empty($php)) {
|
||||
$php = 'php';
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ foreach ($d as $ds) {
|
||||
} else {
|
||||
$price = $p['price'];
|
||||
}
|
||||
if ($ds['expiration'] == $day7 && $config['notification_reminder_7day'] !== 'no') {
|
||||
if ($ds['expiration'] == $day7 && $config['notification_reminder_7days'] !== 'no') {
|
||||
try {
|
||||
echo Message::sendPackageNotification(
|
||||
$c,
|
||||
@ -61,7 +61,7 @@ foreach ($d as $ds) {
|
||||
} 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') {
|
||||
} else if ($ds['expiration'] == $day3 && $config['notification_reminder_3days'] !== 'no') {
|
||||
try {
|
||||
echo Message::sendPackageNotification(
|
||||
$c,
|
||||
|
@ -244,7 +244,7 @@ class MikrotikPppoe
|
||||
|
||||
function add_pool($pool){
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
if ($_app_stage == 'Demo') {
|
||||
return null;
|
||||
}
|
||||
$mikrotik = $this->info($pool['routers']);
|
||||
@ -259,7 +259,7 @@ class MikrotikPppoe
|
||||
|
||||
function update_pool($old_pool, $new_pool){
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
if ($_app_stage == 'Demo') {
|
||||
return null;
|
||||
}
|
||||
$mikrotik = $this->info($new_pool['routers']);
|
||||
@ -284,7 +284,7 @@ class MikrotikPppoe
|
||||
|
||||
function remove_pool($pool){
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
if ($_app_stage == 'Demo') {
|
||||
return null;
|
||||
}
|
||||
$mikrotik = $this->info($pool['routers']);
|
||||
@ -329,7 +329,7 @@ class MikrotikPppoe
|
||||
function getClient($ip, $user, $pass)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
if ($_app_stage == 'Demo') {
|
||||
return null;
|
||||
}
|
||||
$iport = explode(":", $ip);
|
||||
@ -339,7 +339,7 @@ class MikrotikPppoe
|
||||
function removePpoeUser($client, $username)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
if ($_app_stage == 'Demo') {
|
||||
return null;
|
||||
}
|
||||
$printRequest = new RouterOS\Request('/ppp/secret/print');
|
||||
@ -376,7 +376,7 @@ class MikrotikPppoe
|
||||
function removePpoeActive($client, $username)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
if ($_app_stage == 'Demo') {
|
||||
return null;
|
||||
}
|
||||
$onlineRequest = new RouterOS\Request('/ppp/active/print');
|
||||
@ -392,7 +392,7 @@ class MikrotikPppoe
|
||||
function getIpHotspotUser($client, $username)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
if ($_app_stage == 'Demo') {
|
||||
return null;
|
||||
}
|
||||
$printRequest = new RouterOS\Request(
|
||||
@ -405,7 +405,7 @@ class MikrotikPppoe
|
||||
function addIpToAddressList($client, $ip, $listName, $comment = '')
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
if ($_app_stage == 'Demo') {
|
||||
return null;
|
||||
}
|
||||
$addRequest = new RouterOS\Request('/ip/firewall/address-list/add');
|
||||
@ -420,7 +420,7 @@ class MikrotikPppoe
|
||||
function removeIpFromAddressList($client, $ip)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
if ($_app_stage == 'Demo') {
|
||||
return null;
|
||||
}
|
||||
$printRequest = new RouterOS\Request(
|
||||
|
@ -381,5 +381,166 @@
|
||||
"_Clear_old_logs_": " Clear old logs?",
|
||||
"Clean_up_Logs": "Clean up Logs",
|
||||
"ID": "ID",
|
||||
"Date_Sent": "Date Sent"
|
||||
"Date_Sent": "Date Sent",
|
||||
"Notification_Message": "Notification Message",
|
||||
"Share": "Share",
|
||||
"Previous": "Previous",
|
||||
"Email_not_sent__Mailer_Error__": "Email not sent, Mailer Error: ",
|
||||
"Invoice_Lists": "Invoice Lists",
|
||||
"Invoices": "Invoices",
|
||||
"Invoice_No": "Invoice No",
|
||||
"Customer_Name": "Customer Name",
|
||||
"Amount": "Amount",
|
||||
"Due_Date": "Due Date",
|
||||
"Actions": "Actions",
|
||||
"Pending": "Pending",
|
||||
"Send": "Send",
|
||||
"": "",
|
||||
"Commission": "Commission",
|
||||
"Last_30_Days_Overall_Usage": "Last 30 Days Overall Usage",
|
||||
"Last_30_Days_Total_Usage": "Last 30 Days Total Usage",
|
||||
"Top_10_Downloaders": "Top 10 Downloaders",
|
||||
"Rank": "Rank",
|
||||
"Downloaded": "Downloaded",
|
||||
"Upload_Data": "Upload Data",
|
||||
"Download_Data": "Download Data",
|
||||
"Total_Data": "Total Data",
|
||||
"Service": "Service",
|
||||
"Mac_Address": "Mac Address",
|
||||
"Location": "Location",
|
||||
"Last_Updated": "Last Updated",
|
||||
"View_Details": "View Details",
|
||||
"Successful_Payments": "Successful Payments",
|
||||
"Failed_Payments": "Failed Payments",
|
||||
"Pending_Payments": "Pending Payments",
|
||||
"Cancelled_Payments": "Cancelled Payments",
|
||||
"Refresh_Dashboard": "Refresh Dashboard",
|
||||
"Daily_Sales": "Daily Sales",
|
||||
"Monthly_Sales": "Monthly Sales",
|
||||
"Weekly_Sales": "Weekly Sales",
|
||||
"Hotspot_Vouchers_Overview": "Hotspot Vouchers Overview",
|
||||
"Transaction_Ref": "Transaction Ref",
|
||||
"Router_Name": "Router Name",
|
||||
"Voucher_Code": "Voucher Code",
|
||||
"Transaction_Status": "Transaction Status",
|
||||
"Payment_Date": "Payment Date",
|
||||
"Plan_Expiry_Date": "Plan Expiry Date",
|
||||
"Action": "Action",
|
||||
"Block_Mac_Address": "Block Mac Address",
|
||||
"Unblock_Mac_Address": "Unblock Mac Address",
|
||||
"Captive_Portal_Dashboard": "Captive Portal Dashboard",
|
||||
"Manage_Router_NAS": "Manage Router\/NAS",
|
||||
"Captive_Portal_Routers": "Captive Portal Routers",
|
||||
"Add_New_Router": "Add New Router",
|
||||
"Router_Details": "Router Details",
|
||||
"MAP": "MAP",
|
||||
"LANDING_PAGE": "LANDING PAGE",
|
||||
"FILE": "FILE",
|
||||
"EDIT": "EDIT",
|
||||
"Edit_Router": "Edit Router",
|
||||
"Description": "Description",
|
||||
"Enter_Description": "Enter Description",
|
||||
"Coordinate": "Coordinate",
|
||||
"DELETE": "DELETE",
|
||||
"Back_To_Dashboard": "Back To Dashboard",
|
||||
"Device_Type": "Device Type",
|
||||
"Mikrotik_API": "Mikrotik API",
|
||||
"Choose_Router": "Choose Router",
|
||||
"Choose": "Choose",
|
||||
"Select": "Select",
|
||||
"Add_Router": "Add Router",
|
||||
"Router_Location": "Router Location",
|
||||
"Go_Back": "Go Back",
|
||||
"Preview": "Preview",
|
||||
"Login_html": "Login.html",
|
||||
"Branding": "Branding",
|
||||
"Sliders": "Sliders",
|
||||
"Advertisements": "Advertisements",
|
||||
"Announcements": "Announcements",
|
||||
"Integrations": "Integrations",
|
||||
"Basic_Settings": "Basic Settings",
|
||||
"Page_Title": "Page Title",
|
||||
"Displayed_in_browser_title_bar": "Displayed in browser title bar",
|
||||
"Page_description_browser_title_bar": "Page description browser title bar",
|
||||
"Hotspot_Name": "Hotspot Name",
|
||||
"Hotspot_Name_will_be_display_on_Login_Page_Nav_Bar_if_Logo_is_not_available": "Hotspot Name will be display on Login Page Nav Bar if Logo is not available",
|
||||
"Footer_Text": "Footer Text",
|
||||
"Allow_Free_Trial": "Allow Free Trial",
|
||||
"Choose_No_if_you_dont_want_to_allow_Free_Trial": "Choose No if you dont want to allow Free Trial",
|
||||
"Make_sure_you_enable_free_trial_in_Mikrotik_Router": "Make sure you enable free trial in Mikrotik Router",
|
||||
"free_trial_button_wont_display_on_captive_portal_preview__but_will_work_if_you_connect_from_a_hotspot": "free trial button wont display on captive portal preview, but will work if you connect from a hotspot",
|
||||
"Allow_Member_Login": "Allow Member Login",
|
||||
"Choose_No_If_you_want_to_disable_Member_Login": "Choose No If you want to disable Member Login",
|
||||
"Allow_Randomized_MAC": "Allow Randomized MAC",
|
||||
"Choose_Yes_If_you_want_to_allow_Randomized_or_Private_MAC": "Choose Yes If you want to allow Randomized or Private MAC",
|
||||
"Assign_Plan": "Assign Plan",
|
||||
"Service_Plan": "Service Plan",
|
||||
"Change": "Change",
|
||||
"Select_Plans": "Select Plans",
|
||||
"Choose_plan_that_will_that_your_users_will_be_assigned": "Choose plan that will that your users will be assigned",
|
||||
"Restore_Default_Settings": "Restore Default Settings",
|
||||
"Restore_Default": "Restore Default",
|
||||
"Branding_Image": "Branding Image",
|
||||
"Logo": "Logo",
|
||||
"Backgrounds": "Backgrounds",
|
||||
"Background_Type": "Background Type",
|
||||
"Static_Color": "Static Color",
|
||||
"Gradient_Color": "Gradient Color",
|
||||
"Image": "Image",
|
||||
"Static_Background_Color": "Static Background Color",
|
||||
"Gradient_Background_Colors": "Gradient Background Colors",
|
||||
"Gradient_Direction": "Gradient Direction",
|
||||
"Left_to_Right": "Left to Right",
|
||||
"Top_to_Bottom": "Top to Bottom",
|
||||
"Right_to_Left": "Right to Left",
|
||||
"Bottom_to_Top": "Bottom to Top",
|
||||
"Choose_Image": "Choose Image",
|
||||
"Buttons": "Buttons",
|
||||
"Slider_Contact_Us_Button_BG_Color": "Slider Contact Us Button BG Color",
|
||||
"Slider_Contact_Us_Button_Text_Color": "Slider Contact Us Button Text Color",
|
||||
"Slider_Navigation_Button_BG_Color": "Slider Navigation Button BG Color",
|
||||
"Slider_Navigation_Button_Icon_Color": "Slider Navigation Button Icon Color",
|
||||
"Trial_Button_BG_Color": "Trial Button BG Color",
|
||||
"Trial_Button_Text_Color": "Trial Button Text Color",
|
||||
"Footer_Settings": "Footer Settings",
|
||||
"Footer_Background_Color": "Footer Background Color",
|
||||
"Misc": "Misc",
|
||||
"Title": "Title",
|
||||
"Link": "Link",
|
||||
"Button": "Button",
|
||||
"Add_New_Slider": "Add New Slider",
|
||||
"Welcome_to_the_Captive_Portal": "Welcome to the Captive Portal",
|
||||
"Button_Link": "Button Link",
|
||||
"Button_Text": "Button Text",
|
||||
"Click_Here": "Click Here",
|
||||
"Target_Link": "Target Link",
|
||||
"Create_New_Advert": "Create New Advert",
|
||||
"Add_New_Advertisement": "Add New Advertisement",
|
||||
"Content": "Content",
|
||||
"Enter_Interstitial_Content": "Enter Interstitial Content",
|
||||
"Upload_File": "Upload File",
|
||||
"File_size_limit__5MB_for_images__100MB_for_videos": "File size limit: 5MB for images, 100MB for videos",
|
||||
"Target_URL": "Target URL",
|
||||
"Create_Advert": "Create Advert",
|
||||
"You_cannot_perform_this_action_in_Demo_mode": "You cannot perform this action in Demo mode",
|
||||
"Income_Today": "Income Today",
|
||||
"Income_This_Month": "Income This Month",
|
||||
"Customers": "Customers",
|
||||
"Monthly_Registered_Customers": "Monthly Registered Customers",
|
||||
"Cron_has_not_run_for_over_1_hour__Please_check_your_setup_": "Cron has not run for over 1 hour. Please check your setup.",
|
||||
"Total_Monthly_Sales": "Total Monthly Sales",
|
||||
"Customers_Expired__Today": "Customers Expired, Today",
|
||||
"Phone": "Phone",
|
||||
"Created___Expired": "Created \/ Expired",
|
||||
"Internet_Package": "Internet Package",
|
||||
"year": "year",
|
||||
"month": "month",
|
||||
"week": "week",
|
||||
"day": "day",
|
||||
"hour": "hour",
|
||||
"minute": "minute",
|
||||
"second": "second",
|
||||
"ago": "ago",
|
||||
"All_Users_Insights": "All Users Insights",
|
||||
"Activity_Log": "Activity Log"
|
||||
}
|
@ -63,9 +63,7 @@
|
||||
"ALTER TABLE `tbl_plans` ADD `list_expired` VARCHAR(32) NOT NULL DEFAULT '' COMMENT 'address list' AFTER `pool_expired`;",
|
||||
"ALTER TABLE `tbl_bandwidth` ADD `burst` VARCHAR(128) NOT NULL DEFAULT '' AFTER `rate_up_unit`;"
|
||||
],
|
||||
"2024.2.20.1": [
|
||||
"DROP TABLE IF EXISTS `tbl_customers_meta`;"
|
||||
],
|
||||
"2024.2.20.1": ["DROP TABLE IF EXISTS `tbl_customers_meta`;"],
|
||||
"2024.2.23": [
|
||||
"ALTER TABLE `tbl_transactions` ADD `admin_id` INT NOT NULL DEFAULT '1' AFTER `type`;",
|
||||
"ALTER TABLE `tbl_user_recharges` ADD `admin_id` INT NOT NULL DEFAULT '1' AFTER `type`;"
|
||||
@ -191,20 +189,23 @@
|
||||
"2025.2.14": [
|
||||
"CREATE TABLE IF NOT EXISTS `tbl_widgets` ( `id` int NOT NULL AUTO_INCREMENT, `orders` int NOT NULL DEFAULT '99', `position` tinyint(1) NOT NULL DEFAULT '1' COMMENT '1. top 2. left 3. right 4. bottom',`enabled` tinyint(1) NOT NULL DEFAULT '1', `title` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `widget` varchar(64) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `content` text COLLATE utf8mb4_general_ci NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;"
|
||||
],
|
||||
"2025.2.17" : [
|
||||
"2025.2.17": [
|
||||
"INSERT INTO `tbl_widgets` (`id`, `orders`, `position`, `enabled`, `title`, `widget`, `content`) VALUES (1, 1, 1, 1, 'Top Widget', 'top_widget', ''),(2, 2, 1, 1, 'Default Info', 'default_info_row', ''),(3, 1, 2, 1, 'Graph Monthly Registered Customers', 'graph_monthly_registered_customers', ''),(4, 2, 2, 1, 'Graph Monthly Sales', 'graph_monthly_sales', ''),(5, 3, 2, 1, 'Voucher Stocks', 'voucher_stocks', ''),(6, 4, 2, 1, 'Customer Expired', 'customer_expired', ''),(7, 1, 3, 1, 'Cron Monitor', 'cron_monitor', ''),(8, 2, 3, 1, 'Mikrotik Cron Monitor', 'mikrotik_cron_monitor', ''),(9, 3, 3, 1, 'Info Payment Gateway', 'info_payment_gateway', ''),(10, 4, 3, 1, 'Graph Customers Insight', 'graph_customers_insight', ''),(11, 5, 3, 1, 'Activity Log', 'activity_log', '');"
|
||||
],
|
||||
"2025.2.19" : [
|
||||
"2025.2.19": [
|
||||
"ALTER TABLE `tbl_widgets` ADD `user` ENUM('Admin','Agent','Sales','Customer') NOT NULL DEFAULT 'Admin' AFTER `position`;"
|
||||
],
|
||||
"2025.2.21" : [
|
||||
"2025.2.21": [
|
||||
"INSERT INTO `tbl_widgets` (`id`, `orders`, `position`, `user`, `enabled`, `title`, `widget`, `content`) VALUES (60, 1, 2, 'Customer', 1, 'Account Info', 'account_info', ''),(61, 3, 1, 'Customer', 1, 'Active Internet Plan', 'active_internet_plan', ''),(62, 4, 1, 'Customer', 1, 'Balance Transfer', 'balance_transfer', ''),(63, 1, 1, 'Customer', 1, 'Unpaid Order', 'unpaid_order', ''),(64, 2, 1, 'Customer', 1, 'Announcement', 'announcement', ''),(65, 5, 1, 'Customer', 1, 'Recharge A Friend', 'recharge_a_friend', ''),(66, 2, 2, 'Customer', 1, 'Voucher Activation', 'voucher_activation', '');"
|
||||
],
|
||||
"2025.2.25" : [
|
||||
"2025.2.25": [
|
||||
"INSERT INTO `tbl_widgets` (`id`, `orders`, `position`, `user`, `enabled`, `title`, `widget`, `content`) VALUES (30, 1, 1, 'Agent', 1, 'Top Widget', 'top_widget', ''), (31, 2, 1, 'Agent', 1, 'Default Info', 'default_info_row', ''), (32, 1, 2, 'Agent', 1, 'Graph Monthly Registered Customers', 'graph_monthly_registered_customers', ''), (33, 2, 2, 'Agent', 1, 'Graph Monthly Sales', 'graph_monthly_sales', ''), (34, 3, 2, 'Agent', 1, 'Voucher Stocks', 'voucher_stocks', ''), (35, 4, 2, 'Agent', 1, 'Customer Expired', 'customer_expired', ''), (36, 1, 3, 'Agent', 1, 'Cron Monitor', 'cron_monitor', ''), (37, 2, 3, 'Agent', 1, 'Mikrotik Cron Monitor', 'mikrotik_cron_monitor', ''), (38, 3, 3, 'Agent', 1, 'Info Payment Gateway', 'info_payment_gateway', ''), (39, 4, 3, 'Agent', 1, 'Graph Customers Insight', 'graph_customers_insight', ''),(40, 5, 3, 'Agent', 1, 'Activity Log', 'activity_log', '');",
|
||||
"INSERT INTO `tbl_widgets` (`id`, `orders`, `position`, `user`, `enabled`, `title`, `widget`, `content`) VALUES (41, 1, 1, 'Sales', 1, 'Top Widget', 'top_widget', ''), (42, 2, 1, 'Sales', 1, 'Default Info', 'default_info_row', ''), (43, 1, 2, 'Sales', 1, 'Graph Monthly Registered Customers', 'graph_monthly_registered_customers', ''), (44, 2, 2, 'Sales', 1, 'Graph Monthly Sales', 'graph_monthly_sales', ''), (45, 3, 2, 'Sales', 1, 'Voucher Stocks', 'voucher_stocks', ''), (46, 4, 2, 'Sales', 1, 'Customer Expired', 'customer_expired', ''), (47, 1, 3, 'Sales', 1, 'Cron Monitor', 'cron_monitor', ''), (48, 2, 3, 'Sales', 1, 'Mikrotik Cron Monitor', 'mikrotik_cron_monitor', ''), (49, 3, 3, 'Sales', 1, 'Info Payment Gateway', 'info_payment_gateway', ''), (50, 4, 3, 'Sales', 1, 'Graph Customers Insight', 'graph_customers_insight', ''), (51, 5, 3, 'Sales', 1, 'Activity Log', 'activity_log', '');"
|
||||
],
|
||||
"2025.3.5" : [
|
||||
"2025.3.5": [
|
||||
"CREATE TABLE IF NOT EXISTS `tbl_message_logs` ( `id` SERIAL PRIMARY KEY, `message_type` VARCHAR(50), `recipient` VARCHAR(255), `message_content` TEXT, `status` VARCHAR(50), `error_message` TEXT, `sent_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;"
|
||||
],
|
||||
"2025.3.10": [
|
||||
"CREATE TABLE IF NOT EXISTS `tbl_invoices` ( `id` INT AUTO_INCREMENT PRIMARY KEY, `number` VARCHAR(50) NOT NULL, `customer_id` INT NOT NULL, `fullname` VARCHAR(100) NOT NULL, `email` VARCHAR(100) NOT NULL, `address` TEXT, `status` ENUM('Unpaid', 'Paid', 'Cancelled') NOT NULL DEFAULT 'Unpaid', `due_date` DATETIME NOT NULL, `filename` VARCHAR(255), `amount` DECIMAL(10, 2) NOT NULL, `data` JSON NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP);"
|
||||
]
|
||||
}
|
0
system/uploads/invoices/index.html
Normal file
0
system/uploads/invoices/index.html
Normal file
@ -18,9 +18,9 @@
|
||||
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
|
||||
<div class="btn-group pull-right">
|
||||
<a class="btn btn-primary btn-xs" title="save"
|
||||
href="{Text::url('customers/csv&token=', $csrf_token)}"
|
||||
onclick="return ask(this, '{Lang::T("This will export to CSV")}?')"><span
|
||||
class="glyphicon glyphicon-download" aria-hidden="true"></span> CSV</a>
|
||||
href="{Text::url('customers/csv&token=', $csrf_token)}" onclick="return ask(this, '{Lang::T("
|
||||
This will export to CSV")}?')"><span class="glyphicon glyphicon-download"
|
||||
aria-hidden="true"></span> CSV</a>
|
||||
</div>
|
||||
{/if}
|
||||
{Lang::T('Manage Contact')}
|
||||
@ -205,14 +205,15 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<select id="messageType" class="form-control">
|
||||
<select style="margin-bottom: 10px;" id="messageType" class="form-control">
|
||||
<option value="all">{Lang::T('All')}</option>
|
||||
<option value="email">{Lang::T('Email')}</option>
|
||||
<option value="inbox">{Lang::T('Inbox')}</option>
|
||||
<option value="sms">{Lang::T('SMS')}</option>
|
||||
<option value="wa">{Lang::T('WhatsApp')}</option>
|
||||
</select>
|
||||
<br>
|
||||
<input type="text" style="margin-bottom: 10px;" class="form-control" id="subject-content" value=""
|
||||
placeholder="{Lang::T('Enter message subject here')}">
|
||||
<textarea id="messageContent" class="form-control" rows="4"
|
||||
placeholder="{Lang::T('Enter your message here...')}"></textarea>
|
||||
</div>
|
||||
@ -260,6 +261,8 @@
|
||||
$('#sendMessageButton').on('click', function () {
|
||||
const message = $('#messageContent').val().trim();
|
||||
const messageType = $('#messageType').val();
|
||||
const subject = $('#subject-content').val().trim();
|
||||
|
||||
|
||||
if (!message) {
|
||||
Swal.fire({
|
||||
@ -271,6 +274,16 @@
|
||||
return;
|
||||
}
|
||||
|
||||
if (messageType == 'all' || messageType == 'inbox' || messageType == 'email' && !subject) {
|
||||
Swal.fire({
|
||||
title: 'Error!',
|
||||
text: "{Lang::T('Please enter a subject for the message.')}",
|
||||
icon: 'error',
|
||||
confirmButtonText: 'OK'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable the button and show loading text
|
||||
$(this).prop('disabled', true).text('{Lang::T('Sending...')}');
|
||||
|
||||
@ -332,4 +345,31 @@
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
document.getElementById('messageType').addEventListener('change', function () {
|
||||
const messageType = this.value;
|
||||
const subjectField = document.getElementById('subject-content');
|
||||
|
||||
subjectField.style.display = (messageType === 'all' || messageType === 'email' || messageType === 'inbox') ? 'block' : 'none';
|
||||
|
||||
switch (messageType) {
|
||||
case 'all':
|
||||
subjectField.placeholder = 'Enter a subject for all channels';
|
||||
subjectField.required = true;
|
||||
break;
|
||||
case 'email':
|
||||
subjectField.placeholder = 'Enter a subject for email';
|
||||
subjectField.required = true;
|
||||
break;
|
||||
case 'inbox':
|
||||
subjectField.placeholder = 'Enter a subject for inbox';
|
||||
subjectField.required = true;
|
||||
break;
|
||||
default:
|
||||
subjectField.placeholder = 'Enter message subject here';
|
||||
subjectField.required = false;
|
||||
break;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{include file = "sections/footer.tpl" }
|
@ -14,6 +14,7 @@
|
||||
<link rel="stylesheet" href="{$app_url}/ui/ui/styles/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="{$app_url}/ui/ui/fonts/ionicons/css/ionicons.min.css">
|
||||
<link rel="stylesheet" href="{$app_url}/ui/ui/fonts/font-awesome/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="{$app_url}/ui/ui/styles/modern-AdminLTE.min.css">
|
||||
<link rel="stylesheet" href="{$app_url}/ui/ui/styles/select2.min.css" />
|
||||
<link rel="stylesheet" href="{$app_url}/ui/ui/styles/select2-bootstrap.min.css" />
|
||||
@ -77,8 +78,8 @@
|
||||
<ul class="dropdown-menu">
|
||||
<li class="user-header">
|
||||
<img src="{$app_url}/{$UPLOAD_PATH}{$_admin['photo']}.thumb.jpg"
|
||||
onerror="this.src='{$app_url}/{$UPLOAD_PATH}/admin.default.png'" class="img-circle"
|
||||
alt="Avatar">
|
||||
onerror="this.src='{$app_url}/{$UPLOAD_PATH}/admin.default.png'"
|
||||
class="img-circle" alt="Avatar">
|
||||
<p>
|
||||
{$_admin['fullname']}
|
||||
<small>{Lang::T($_admin['user_type'])}</small>
|
||||
@ -173,7 +174,8 @@
|
||||
href="{Text::url('services/hotspot')}">Hotspot</a></li>
|
||||
<li {if $_routes[1] eq 'pppoe' }class="active" {/if}><a
|
||||
href="{Text::url('services/pppoe')}">PPPOE</a></li>
|
||||
<li {if $_routes[1] eq 'vpn' }class="active" {/if}><a href="{Text::url('services/vpn')}">VPN</a>
|
||||
<li {if $_routes[1] eq 'vpn' }class="active" {/if}><a
|
||||
href="{Text::url('services/vpn')}">VPN</a>
|
||||
</li>
|
||||
<li {if $_routes[1] eq 'list' }class="active" {/if}><a
|
||||
href="{Text::url('bandwidth/list')}">Bandwidth</a></li>
|
||||
@ -215,6 +217,8 @@
|
||||
href="{Text::url('reports')}">{Lang::T('Daily Reports')}</a></li>
|
||||
<li {if $_routes[1] eq 'activation' }class="active" {/if}><a
|
||||
href="{Text::url('reports/activation')}">{Lang::T('Activation History')}</a></li>
|
||||
{* <li {if $_routes[0] eq 'invoices' }class="active" {/if}><a
|
||||
href="{Text::url('invoices')}">{Lang::T('Invoices')}</a></li> *}
|
||||
{$_MENU_REPORTS}
|
||||
</ul>
|
||||
</li>
|
||||
@ -285,16 +289,19 @@
|
||||
<li {if $_routes[1] eq 'Announcement' }class="active" {/if}><a
|
||||
href="{Text::url('pages/Announcement')}">{Lang::T('Announcement')}</a></li>
|
||||
<li {if $_routes[1] eq 'Announcement_Customer' }class="active" {/if}><a
|
||||
href="{Text::url('pages/Announcement_Customer')}">{Lang::T('Customer Announcement')}</a>
|
||||
href="{Text::url('pages/Announcement_Customer')}">{Lang::T('Customer
|
||||
Announcement')}</a>
|
||||
</li>
|
||||
<li {if $_routes[1] eq 'Registration_Info' }class="active" {/if}><a
|
||||
href="{Text::url('pages/Registration_Info')}">{Lang::T('Registration Info')}</a></li>
|
||||
href="{Text::url('pages/Registration_Info')}">{Lang::T('Registration Info')}</a>
|
||||
</li>
|
||||
<li {if $_routes[1] eq 'Payment_Info' }class="active" {/if}><a
|
||||
href="{Text::url('pages/Payment_Info')}">{Lang::T('Payment Info')}</a></li>
|
||||
<li {if $_routes[1] eq 'Privacy_Policy' }class="active" {/if}><a
|
||||
href="{Text::url('pages/Privacy_Policy')}">{Lang::T('Privacy Policy')}</a></li>
|
||||
<li {if $_routes[1] eq 'Terms_and_Conditions' }class="active" {/if}><a
|
||||
href="{Text::url('pages/Terms_and_Conditions')}">{Lang::T('Terms and Conditions')}</a></li>
|
||||
href="{Text::url('pages/Terms_and_Conditions')}">{Lang::T('Terms and
|
||||
Conditions')}</a></li>
|
||||
{$_MENU_PAGES}
|
||||
</ul>
|
||||
</li>
|
||||
@ -373,7 +380,8 @@
|
||||
{$_MENU_AFTER_LOGS}
|
||||
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
|
||||
<li {if $_routes[1] eq 'docs' }class="active" {/if}>
|
||||
<a href="{if $_c['docs_clicked'] != 'yes'}{Text::url('settings/docs')}{else}{$app_url}/docs{/if}">
|
||||
<a
|
||||
href="{if $_c['docs_clicked'] != 'yes'}{Text::url('settings/docs')}{else}{$app_url}/docs{/if}">
|
||||
<i class="ion ion-ios-bookmarks"></i>
|
||||
<span class="text">{Lang::T('Documentation')}</span>
|
||||
{if $_c['docs_clicked'] != 'yes'}
|
||||
@ -426,4 +434,4 @@
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{/if}
|
||||
{/if}
|
57
ui/ui/admin/invoices/list.tpl
Normal file
57
ui/ui/admin/invoices/list.tpl
Normal file
@ -0,0 +1,57 @@
|
||||
{include file="sections/header.tpl"}
|
||||
|
||||
<!-- Add a Table for Sent History -->
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">{Lang::T('Invoices')}</div>
|
||||
<div class="panel-body" style="overflow: auto;">
|
||||
<table class="table table-bordered" id="invoiceTable" style="width:100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{Lang::T('Invoice No')}</th>
|
||||
<th>{Lang::T('Customer Name')}</th>
|
||||
<th>{Lang::T('Email')}</th>
|
||||
<th>{Lang::T('Address')}</th>
|
||||
<th>{Lang::T('Amount')}</th>
|
||||
<th>{Lang::T('Status')}</th>
|
||||
<th>{Lang::T('Created Date')}</th>
|
||||
<th>{Lang::T('Due Date')}</th>
|
||||
<th>{Lang::T('Actions')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach $invoices as $invoice}
|
||||
<tr>
|
||||
<td>{$invoice->number}</td>
|
||||
<td>{$invoice->fullname}</td>
|
||||
<td>{$invoice->email}</td>
|
||||
<td>{$invoice->address}</td>
|
||||
<td>{$invoice->amount}</td>
|
||||
<td>
|
||||
{if $invoice->status == 'paid'}
|
||||
<span class="label label-success">{Lang::T('Paid')}</span>
|
||||
{elseif $invoice->status == 'unpaid'}
|
||||
<span class="label label-danger">{Lang::T('Unpaid')}</span>
|
||||
{else}
|
||||
<span class="label label-warning">{Lang::T('Pending')}</span>
|
||||
{/if}
|
||||
</td>
|
||||
<td>{$invoice->created_at}</td>
|
||||
<td>{$invoice->due_date}</td>
|
||||
<td>
|
||||
<a href="{$app_url}/system/uploads/invoices/{$invoice->filename}" class="btn btn-primary btn-xs">{Lang::T('View')}</a>
|
||||
<!-- <a href="javascript:void(0);" class="btn btn-danger btn-xs" onclick="deleteInvoice({$invoice->id});">{Lang::T('Delete')}</a>
|
||||
<a href="javascript:void(0);" class="btn btn-success btn-xs" onclick="sendInvoice({$invoice->id});">{Lang::T('Send')}</a> -->
|
||||
</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<script src="https://cdn.datatables.net/1.11.3/js/jquery.dataTables.min.js"></script>
|
||||
<script>
|
||||
new DataTable('#invoiceTable');
|
||||
</script>
|
||||
|
||||
{include file="sections/footer.tpl"}
|
@ -30,7 +30,8 @@
|
||||
<select class="form-control" name="service" id="service">
|
||||
<option value="all" {if $group=='all' }selected{/if}>{Lang::T('All')}</option>
|
||||
<option value="PPPoE" {if $service=='PPPoE' }selected{/if}>{Lang::T('PPPoE')}</option>
|
||||
<option value="Hotspot" {if $service=='Hotspot' }selected{/if}>{Lang::T('Hotspot')}</option>
|
||||
<option value="Hotspot" {if $service=='Hotspot' }selected{/if}>{Lang::T('Hotspot')}
|
||||
</option>
|
||||
<option value="VPN" {if $service=='VPN' }selected{/if}>{Lang::T('VPN')}</option>
|
||||
</select>
|
||||
</div>
|
||||
@ -41,8 +42,10 @@
|
||||
<select class="form-control" name="group" id="group">
|
||||
<option value="all" {if $group=='all' }selected{/if}>{Lang::T('All Customers')}</option>
|
||||
<option value="new" {if $group=='new' }selected{/if}>{Lang::T('New Customers')}</option>
|
||||
<option value="expired" {if $group=='expired' }selected{/if}>{Lang::T('Expired Customers')}</option>
|
||||
<option value="active" {if $group=='active' }selected{/if}>{Lang::T('Active Customers')}</option>
|
||||
<option value="expired" {if $group=='expired' }selected{/if}>{Lang::T('Expired
|
||||
Customers')}</option>
|
||||
<option value="active" {if $group=='active' }selected{/if}>{Lang::T('Active Customers')}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@ -50,9 +53,13 @@
|
||||
<label class="col-md-2 control-label">{Lang::T('Send Via')}</label>
|
||||
<div class="col-md-6">
|
||||
<select class="form-control" name="via" id="via">
|
||||
<option value="all" {if $via=='all' }selected{/if}>{Lang::T('All Channels')}</option>
|
||||
<option value="inbox" {if $via=='inbox' }selected{/if}>{Lang::T('Inbox')}</option>
|
||||
<option value="email" {if $via=='email' }selected{/if}>{Lang::T('Email')}</option>
|
||||
<option value="sms" {if $via=='sms' }selected{/if}>{Lang::T('SMS')}</option>
|
||||
<option value="wa" {if $via=='wa' }selected{/if}>{Lang::T('WhatsApp')}</option>
|
||||
<option value="both" {if $via=='both' }selected{/if}>{Lang::T('SMS and WhatsApp')}</option>
|
||||
<option value="both" {if $via=='both' }selected{/if}>{Lang::T('SMS and WhatsApp')}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@ -72,10 +79,21 @@
|
||||
{Lang::T('Use 20 and above if you are sending to all customers to avoid server time out')}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" id="subject-content">
|
||||
<label class="col-md-2 control-label">{Lang::T('Subject')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" name="subject" id="subject" value=""
|
||||
placeholder="{Lang::T('Enter message subject here')}">
|
||||
</div>
|
||||
<p class="help-block col-md-4">
|
||||
{Lang::T('You can also use the below placeholders here too')}.
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label">{Lang::T('Message')}</label>
|
||||
<div class="col-md-6">
|
||||
<textarea class="form-control" id="message" name="message" required placeholder="{Lang::T('Compose your message...')}" rows="5">{$message}</textarea>
|
||||
<textarea class="form-control" id="message" name="message" required
|
||||
placeholder="{Lang::T('Compose your message...')}" rows="5">{$message}</textarea>
|
||||
<input name="test" id="test" type="checkbox">
|
||||
{Lang::T('Testing [if checked no real message is sent]')}
|
||||
</div>
|
||||
@ -93,7 +111,8 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-lg-offset-2 col-lg-10">
|
||||
<button type="button" id="startBulk" class="btn btn-primary">{Lang::T('Start Bulk Messaging')}</button>
|
||||
<button type="button" id="startBulk" class="btn btn-primary">{Lang::T('Start Bulk
|
||||
Messaging')}</button>
|
||||
<a href="{Text::url('dashboard')}" class="btn btn-default">{Lang::T('Cancel')}</a>
|
||||
</div>
|
||||
</div>
|
||||
@ -112,7 +131,7 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{Lang::T('Customer')}</th>
|
||||
<th>{Lang::T('Phone')}</th>
|
||||
<th>{Lang::T('Channel')}</th>
|
||||
<th>{Lang::T('Status')}</th>
|
||||
<th>{Lang::T('Message')}</th>
|
||||
<th>{Lang::T('Router')}</th>
|
||||
@ -126,6 +145,34 @@
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<script src="https://cdn.datatables.net/1.11.3/js/jquery.dataTables.min.js"></script>
|
||||
<script>
|
||||
document.getElementById('via').addEventListener('change', function () {
|
||||
const via = this.value;
|
||||
const subject = document.getElementById('subject-content');
|
||||
const subjectField = document.getElementById('subject');
|
||||
|
||||
subject.style.display = (via === 'all' || via === 'email' || via === 'inbox') ? 'block' : 'none';
|
||||
|
||||
switch (via) {
|
||||
case 'all':
|
||||
subjectField.placeholder = 'Enter a subject for all channels';
|
||||
subjectField.required = true;
|
||||
break;
|
||||
case 'email':
|
||||
subjectField.placeholder = 'Enter a subject for email';
|
||||
subjectField.required = true;
|
||||
break;
|
||||
case 'inbox':
|
||||
subjectField.placeholder = 'Enter a subject for inbox';
|
||||
subjectField.required = true;
|
||||
break;
|
||||
default:
|
||||
subjectField.placeholder = 'Enter message subject here';
|
||||
subjectField.required = false;
|
||||
break;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{literal}
|
||||
<script>
|
||||
let page = 0;
|
||||
@ -158,6 +205,7 @@
|
||||
page: page,
|
||||
test: $('#test').is(':checked') ? 'on' : 'off',
|
||||
service: $('#service').val(),
|
||||
subject: $('#subject').val(),
|
||||
},
|
||||
dataType: 'json',
|
||||
beforeSend: function () {
|
||||
@ -186,7 +234,7 @@
|
||||
let statusClass = msg.status.includes('Failed') ? 'danger' : 'success';
|
||||
historyTable.row.add([
|
||||
msg.name,
|
||||
msg.phone,
|
||||
msg.channel,
|
||||
`<span class="text-${statusClass}">${msg.status}</span>`,
|
||||
msg.message || 'No message',
|
||||
msg.router ? msg.router : 'All Router',
|
||||
@ -207,7 +255,7 @@
|
||||
console.error("Unexpected response format:", response);
|
||||
$('#status').html(`
|
||||
<div class="alert alert-danger">
|
||||
<i class="fas fa-exclamation-circle"></i> Error: Unexpected response format.
|
||||
<i class="fas fa-exclamation-circle"></i> Error: ${response.message}
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
|
@ -23,11 +23,25 @@
|
||||
<label class="col-md-2 control-label">{Lang::T('Send Via')}</label>
|
||||
<div class="col-md-6">
|
||||
<select class="form-control" name="via" id="via">
|
||||
<option value="sms" selected> {Lang::T('via SMS')}</option>
|
||||
<option value="wa"> {Lang::T('Via WhatsApp')}</option>
|
||||
<option value="both"> {Lang::T('Via WhatsApp and SMS')}</option>
|
||||
<option value="all" {if $via=='all' }selected{/if}>{Lang::T('All Channels')}</option>
|
||||
<option value="inbox" {if $via=='inbox' }selected{/if}>{Lang::T('Inbox')}</option>
|
||||
<option value="email" {if $via=='email' }selected{/if}>{Lang::T('Email')}</option>
|
||||
<option value="sms" {if $via=='sms' }selected{/if}>{Lang::T('SMS')}</option>
|
||||
<option value="wa" {if $via=='wa' }selected{/if}>{Lang::T('WhatsApp')}</option>
|
||||
<option value="both" {if $via=='both' }selected{/if}>{Lang::T('SMS and WhatsApp')}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" id="subject">
|
||||
<label class="col-md-2 control-label">{Lang::T('Subject')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" name="subject" id="subject-content" value=""
|
||||
placeholder="{Lang::T('Enter message subject here')}">
|
||||
</div>
|
||||
<p class="help-block col-md-4">
|
||||
{Lang::T('You can also use the below placeholders here too')}.
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label">{Lang::T('Message')}</label>
|
||||
@ -64,6 +78,33 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
document.getElementById('via').addEventListener('change', function () {
|
||||
const via = this.value;
|
||||
const subject = document.getElementById('subject');
|
||||
const subjectField = document.getElementById('subject-content');
|
||||
|
||||
subject.style.display = (via === 'all' || via === 'email' || via === 'inbox') ? 'block' : 'none';
|
||||
|
||||
switch (via) {
|
||||
case 'all':
|
||||
subjectField.placeholder = 'Enter a subject for all channels';
|
||||
subjectField.required = true;
|
||||
break;
|
||||
case 'email':
|
||||
subjectField.placeholder = 'Enter a subject for email';
|
||||
subjectField.required = true;
|
||||
break;
|
||||
case 'inbox':
|
||||
subjectField.placeholder = 'Enter a subject for inbox';
|
||||
subjectField.required = true;
|
||||
break;
|
||||
default:
|
||||
subjectField.placeholder = 'Enter message subject here';
|
||||
subjectField.required = false;
|
||||
break;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
{include file="sections/footer.tpl"}
|
@ -26,6 +26,7 @@
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-condensed table-striped " style="background: #ffffff">
|
||||
<th class="text-center">{Lang::T('Username')}</th>
|
||||
<th class="text-center">{Lang::T('Fullname')}</th>
|
||||
<th class="text-center">{Lang::T('Plan Name')}</th>
|
||||
<th class="text-center">{Lang::T('Type')}</th>
|
||||
<th class="text-center">{Lang::T('Plan Price')}</th>
|
||||
@ -36,6 +37,7 @@
|
||||
{foreach $d as $ds}
|
||||
<tr>
|
||||
<td>{$ds['username']}</td>
|
||||
<td>{$ds['fullname']}</td>
|
||||
<td class="text-center">{$ds['plan_name']}</td>
|
||||
<td class="text-center">{$ds['type']}</td>
|
||||
<td class="text-right">{Lang::moneyFormat($ds['price'])}</td>
|
||||
|
@ -33,6 +33,7 @@
|
||||
<tr>
|
||||
<th>{Lang::T('Invoice')}</th>
|
||||
<th>{Lang::T('Username')}</th>
|
||||
<th>{Lang::T('Fullname')}</th>
|
||||
<th>{Lang::T('Plan Name')}</th>
|
||||
<th>{Lang::T('Plan Price')}</th>
|
||||
<th>{Lang::T('Type')}</th>
|
||||
@ -48,6 +49,7 @@
|
||||
style="cursor:pointer;">{$ds['invoice']}</td>
|
||||
<td onclick="window.location.href = '{Text::url('')}customers/viewu/{$ds['username']}'"
|
||||
style="cursor:pointer;">{$ds['username']}</td>
|
||||
<td>{$ds['fullname']}</td>
|
||||
<td>{$ds['plan_name']}</td>
|
||||
<td>{Lang::moneyFormat($ds['price'])}</td>
|
||||
<td>{$ds['type']}</td>
|
||||
|
@ -94,10 +94,11 @@
|
||||
<a href="{Text::url('export/pdf-by-date&')}{$filter}" class="btn btn-default"><i
|
||||
class="fa fa-file-pdf-o"></i></a>
|
||||
</th>
|
||||
<th colspan="7"></th>
|
||||
<th colspan="8"></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{Lang::T('Username')}</th>
|
||||
<th>{Lang::T('Fullname')}</th>
|
||||
<th>{Lang::T('Type')}</th>
|
||||
<th>{Lang::T('Plan Name')}</th>
|
||||
<th>{Lang::T('Plan Price')}</th>
|
||||
@ -111,6 +112,7 @@
|
||||
{foreach $d as $ds}
|
||||
<tr>
|
||||
<td>{$ds['username']}</td>
|
||||
<td>{$ds['fullname']}</td>
|
||||
<td>{$ds['type']}</td>
|
||||
<td>{$ds['plan_name']}</td>
|
||||
<td class="text-right">{Lang::moneyFormat($ds['price'])}</td>
|
||||
@ -122,9 +124,8 @@
|
||||
{/foreach}
|
||||
<tr>
|
||||
<th>{Lang::T('Total')}</th>
|
||||
<td colspan="2"></td>
|
||||
<th class="text-right">{Lang::moneyFormat($dr)}</th>
|
||||
<td colspan="4"></td>
|
||||
<td colspan="7"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -37,6 +37,7 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{Lang::T('Username')}</th>
|
||||
<th>{Lang::T('Fullname')}</th>
|
||||
<th>{Lang::T('Type')}</th>
|
||||
<th>{Lang::T('Plan Name')}</th>
|
||||
<th>{Lang::T('Plan Price')}</th>
|
||||
@ -50,6 +51,7 @@
|
||||
{foreach $d as $ds}
|
||||
<tr>
|
||||
<td>{$ds['username']}</td>
|
||||
<td>{$ds['fullname']}</td>
|
||||
<td>{$ds['type']}</td>
|
||||
<td>{$ds['plan_name']}</td>
|
||||
<td class="text-right">{Lang::moneyFormat($ds['price'])}</td>
|
||||
|
@ -155,7 +155,7 @@
|
||||
<h3 class="panel-title">
|
||||
<a role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseLoginPage"
|
||||
aria-expanded="true" aria-controls="collapseLoginPage">
|
||||
{Lang::T('Customer Login Page Settings')}
|
||||
{Lang::T('Customer Login Page')}
|
||||
</a>
|
||||
</h3>
|
||||
</div>
|
||||
@ -254,6 +254,37 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-heading" role="tab" id="Coupon">
|
||||
<h4 class="panel-title">
|
||||
<a class="collapsed" role="button" data-toggle="collapse" data-parent="#accordion"
|
||||
href="#collapseCoupon" aria-expanded="false" aria-controls="collapseCoupon">
|
||||
{Lang::T('Coupons')}
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="collapseCoupon" class="panel-collapse collapse" role="tabpanel">
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<label class="col-md-3 control-label">{Lang::T('Enable Coupon')}</label>
|
||||
<div class="col-md-5">
|
||||
<select name="enable_coupons" id="enable_coupons" class="form-control text-muted">
|
||||
<option value="no">{Lang::T('No')}</option>
|
||||
<option value="yes" {if $_c['enable_coupons'] == 'yes'}selected="selected" {/if}>{Lang::T('Yes')}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<p class="help-block col-md-4">
|
||||
<small>{Lang::T('Enable or disable coupons')}</small>
|
||||
</p>
|
||||
</div>
|
||||
<button class="btn btn-success btn-block" type="submit">
|
||||
{Lang::T('Save Changes')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-heading" role="tab" id="Registration">
|
||||
<h4 class="panel-title">
|
||||
|
@ -1,16 +1,16 @@
|
||||
</section>
|
||||
</div>
|
||||
{if isset($_c['CompanyFooter'])}
|
||||
<footer class="main-footer">
|
||||
<footer class="main-footer">
|
||||
{$_c['CompanyFooter']}
|
||||
<div class="pull-right">
|
||||
<a href="javascript:showPrivacy()">Privacy</a>
|
||||
•
|
||||
<a href="javascript:showTaC()">T & C</a>
|
||||
</div>
|
||||
</footer>
|
||||
</footer>
|
||||
{else}
|
||||
<footer class="main-footer">
|
||||
<footer class="main-footer">
|
||||
PHPNuxBill by <a href="https://github.com/hotspotbilling/phpnuxbill" rel="nofollow noreferrer noopener"
|
||||
target="_blank">iBNuX</a>, Theme by <a href="https://adminlte.io/" rel="nofollow noreferrer noopener"
|
||||
target="_blank">AdminLTE</a>
|
||||
@ -19,7 +19,7 @@
|
||||
•
|
||||
<a href="javascript:showTaC()">T & C</a>
|
||||
</div>
|
||||
</footer>
|
||||
</footer>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@ -50,22 +50,22 @@
|
||||
<script src="{$app_url}/ui/ui/scripts/custom.js?2025.2.5"></script>
|
||||
|
||||
{if isset($xfooter)}
|
||||
{$xfooter}
|
||||
{$xfooter}
|
||||
{/if}
|
||||
|
||||
{if $_c['tawkto'] != ''}
|
||||
<!--Start of Tawk.to Script-->
|
||||
<script type="text/javascript">
|
||||
<!--Start of Tawk.to Script-->
|
||||
<script type="text/javascript">
|
||||
var isLoggedIn = false;
|
||||
var Tawk_API = {
|
||||
onLoad: function() {
|
||||
onLoad: function () {
|
||||
Tawk_API.setAttributes({
|
||||
'username' : '{$_user['username']}',
|
||||
'service' : '{$_user['service_type']}',
|
||||
'balance' : '{$_user['balance']}',
|
||||
'account' : '{$_user['account_type']}',
|
||||
'phone' : '{$_user['phonenumber']}'
|
||||
}, function(error) {
|
||||
'username': '{$_user['username']}',
|
||||
'service': '{$_user['service_type']}',
|
||||
'balance': '{$_user['balance']}',
|
||||
'account': '{$_user['account_type']}',
|
||||
'phone': '{$_user['phonenumber']}'
|
||||
}, function (error) {
|
||||
console.log(error)
|
||||
});
|
||||
|
||||
@ -77,7 +77,7 @@
|
||||
email: '{$_user['email']}',
|
||||
phone: '{$_user['phonenumber']}'
|
||||
};
|
||||
(function() {
|
||||
(function () {
|
||||
var s1 = document.createElement("script"),
|
||||
s0 = document.getElementsByTagName("script")[0];
|
||||
s1.async = true;
|
||||
@ -86,11 +86,11 @@
|
||||
s1.setAttribute('crossorigin', '*');
|
||||
s0.parentNode.insertBefore(s1, s0);
|
||||
})();
|
||||
</script>
|
||||
<!--End of Tawk.to Script-->
|
||||
{/if}
|
||||
</script>
|
||||
<!--End of Tawk.to Script-->
|
||||
{/if}
|
||||
|
||||
<script>
|
||||
<script>
|
||||
const toggleIcon = document.getElementById('toggleIcon');
|
||||
const body = document.body;
|
||||
const savedMode = localStorage.getItem('mode');
|
||||
@ -118,22 +118,22 @@
|
||||
localStorage.setItem('mode', 'dark');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</script>
|
||||
|
||||
|
||||
{literal}
|
||||
<script>
|
||||
<script>
|
||||
var listAtts = document.querySelectorAll(`[api-get-text]`);
|
||||
listAtts.forEach(function(el) {
|
||||
$.get(el.getAttribute('api-get-text'), function(data) {
|
||||
listAtts.forEach(function (el) {
|
||||
$.get(el.getAttribute('api-get-text'), function (data) {
|
||||
el.innerHTML = data;
|
||||
});
|
||||
});
|
||||
$(document).ready(function() {
|
||||
$(document).ready(function () {
|
||||
var listAtts = document.querySelectorAll(`button[type="submit"]`);
|
||||
listAtts.forEach(function(el) {
|
||||
listAtts.forEach(function (el) {
|
||||
if (el.addEventListener) { // all browsers except IE before version 9
|
||||
el.addEventListener("click", function() {
|
||||
el.addEventListener("click", function () {
|
||||
$(this).html(
|
||||
`<span class="loading"></span>`
|
||||
);
|
||||
@ -143,7 +143,7 @@
|
||||
}, false);
|
||||
} else {
|
||||
if (el.attachEvent) { // IE before version 9
|
||||
el.attachEvent("click", function() {
|
||||
el.attachEvent("click", function () {
|
||||
$(this).html(
|
||||
`<span class="loading"></span>`
|
||||
);
|
||||
@ -153,28 +153,47 @@
|
||||
});
|
||||
}
|
||||
}
|
||||
$(function() {
|
||||
$(function () {
|
||||
$('[data-toggle="tooltip"]').tooltip()
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
function ask(field, text){
|
||||
var txt = field.innerHTML;
|
||||
if (confirm(text)) {
|
||||
function ask(field, text) {
|
||||
const txt = field.innerHTML;
|
||||
|
||||
field.innerHTML = `<span class="loading"></span>`;
|
||||
field.setAttribute("disabled", true);
|
||||
|
||||
Swal.fire({
|
||||
title: 'Are you sure?',
|
||||
text: text,
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Yes, proceed',
|
||||
cancelButtonText: 'Cancel',
|
||||
}).then((result) => {
|
||||
let delay = result.isConfirmed ? 400 : 500;
|
||||
|
||||
setTimeout(() => {
|
||||
field.innerHTML = field.innerHTML.replace(`<span class="loading"></span>`, txt);
|
||||
field.innerHTML = txt;
|
||||
field.removeAttribute("disabled");
|
||||
}, 5000);
|
||||
return true;
|
||||
|
||||
if (result.isConfirmed) {
|
||||
const form = field.closest('form');
|
||||
if (form) {
|
||||
form.submit(); // manually submit the form
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
field.innerHTML = field.innerHTML.replace(`<span class="loading"></span>`, txt);
|
||||
field.removeAttribute("disabled");
|
||||
}, 500);
|
||||
return false;
|
||||
//fallback if not in a form
|
||||
const href = field.getAttribute("href") || field.dataset.href;
|
||||
if (href) window.location.href = href;
|
||||
}
|
||||
}
|
||||
}, delay);
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function setCookie(name, value, days) {
|
||||
var expires = "";
|
||||
@ -196,10 +215,10 @@
|
||||
}
|
||||
return null;
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
{/literal}
|
||||
<script>
|
||||
setCookie('user_language', '{$user_language}', 365);
|
||||
setCookie('user_language', '{$user_language}', 365);
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user