Enhancement and Improvements

Refactor CSRF class: improve token handling and update session variable names

Replace bulk message with ajax based message sending.
Added support for multiple recipients in bulk message, and also router based filtering.

Add support for multiple recipients in bulk message from customer list as requested by one of our Member. you can now send messages to multiple recipients at once from customer list.

Added Exception for CRON but not tested yet. i dont have multiple routers.
Added notify to know if cron has been executed or not.
This commit is contained in:
Focuslinks Digital Solutions
2025-02-09 16:06:59 +01:00
parent 60d945d87f
commit 0a3205915f
5 changed files with 863 additions and 493 deletions

View File

@ -16,12 +16,12 @@
<div class="panel panel-hovered mb20 panel-primary">
<div class="panel-heading">
{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, 'This will export to CSV?')"><span
class="glyphicon glyphicon-download" aria-hidden="true"></span> CSV</a>
</div>
<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, 'This will export to CSV?')"><span
class="glyphicon glyphicon-download" aria-hidden="true"></span> CSV</a>
</div>
{/if}
{Lang::T('Manage Contact')}
</div>
@ -65,8 +65,8 @@
<span class="input-group-addon">Status</span>
<select class="form-control" id="filter" name="filter">
{foreach $statuses as $status}
<option value="{$status}" {if $filter eq $status }selected{/if}>{Lang::T($status)}
</option>
<option value="{$status}" {if $filter eq $status }selected{/if}>{Lang::T($status)}
</option>
{/foreach}
</select>
</div>
@ -97,6 +97,7 @@
<table id="customerTable" class="table table-bordered table-striped table-condensed">
<thead>
<tr>
<th><input type="checkbox" id="select-all"></th>
<th>{Lang::T('Username')}</th>
<th>Photo</th>
<th>{Lang::T('Account Type')}</th>
@ -113,65 +114,222 @@
</thead>
<tbody>
{foreach $d as $ds}
<tr {if $ds['status'] != 'Active'}class="danger" {/if}>
<td onclick="window.location.href = '{Text::url('customers/view/', $ds['id'])}'"
style="cursor:pointer;">{$ds['username']}</td>
<td>
<a href="{$app_url}/{$UPLOAD_PATH}{$ds['photo']}" target="photo">
<img src="{$app_url}/{$UPLOAD_PATH}{$ds['photo']}.thumb.jpg" width="32" alt="">
</a>
</td>
<td>{$ds['account_type']}</td>
<td onclick="window.location.href = '{Text::url('customers/view/', $ds['id'])}'"
style="cursor: pointer;">{$ds['fullname']}</td>
<td>{Lang::moneyFormat($ds['balance'])}</td>
<td align="center">
{if $ds['phonenumber']}
<a href="tel:{$ds['phonenumber']}" class="btn btn-default btn-xs"
title="{$ds['phonenumber']}"><i class="glyphicon glyphicon-earphone"></i></a>
{/if}
{if $ds['email']}
<a href="mailto:{$ds['email']}" class="btn btn-default btn-xs"
title="{$ds['email']}"><i class="glyphicon glyphicon-envelope"></i></a>
{/if}
{if $ds['coordinates']}
<a href="https://www.google.com/maps/dir//{$ds['coordinates']}/" target="_blank"
class="btn btn-default btn-xs" title="{$ds['coordinates']}"><i
class="glyphicon glyphicon-map-marker"></i></a>
{/if}
</td>
<td align="center" api-get-text="{Text::url('autoload/plan_is_active/')}{$ds['id']}">
<span class="label label-default">&bull;</span>
</td>
<td>{$ds['service_type']}</td>
<td>
{$ds['pppoe_username']}
{if !empty($ds['pppoe_username']) && !empty($ds['pppoe_ip'])}:{/if}
{$ds['pppoe_ip']}
</td>
<td>{Lang::T($ds['status'])}</td>
<td>{Lang::dateTimeFormat($ds['created_at'])}</td>
<td align="center">
<a href="{Text::url('customers/view/')}{$ds['id']}" id="{$ds['id']}"
style="margin: 0px; color:black"
class="btn btn-success btn-xs">&nbsp;&nbsp;{Lang::T('View')}&nbsp;&nbsp;</a>
<a href="{Text::url('customers/edit/', $ds['id'], '&token=', $csrf_token)}"
id="{$ds['id']}" style="margin: 0px; color:black"
class="btn btn-info btn-xs">&nbsp;&nbsp;{Lang::T('Edit')}&nbsp;&nbsp;</a>
<a href="{Text::url('customers/sync/', $ds['id'], '&token=', $csrf_token)}"
id="{$ds['id']}" style="margin: 5px; color:black"
class="btn btn-success btn-xs">&nbsp;&nbsp;{Lang::T('Sync')}&nbsp;&nbsp;</a>
<a href="{Text::url('plan/recharge/', $ds['id'], '&token=', $csrf_token)}" id="{$ds['id']}"
style="margin: 0px;" class="btn btn-primary btn-xs">{Lang::T('Recharge')}</a>
</td>
</tr>
<tr {if $ds['status'] !='Active' }class="danger" {/if}>
<td><input type="checkbox" name="customer_ids[]" value="{$ds['id']}"></td>
<td onclick="window.location.href = '{Text::url('customers/view/', $ds['id'])}'"
style="cursor:pointer;">{$ds['username']}</td>
<td>
<a href="{$app_url}/{$UPLOAD_PATH}{$ds['photo']}" target="photo">
<img src="{$app_url}/{$UPLOAD_PATH}{$ds['photo']}.thumb.jpg" width="32" alt="">
</a>
</td>
<td>{$ds['account_type']}</td>
<td onclick="window.location.href = '{Text::url('customers/view/', $ds['id'])}'"
style="cursor: pointer;">{$ds['fullname']}</td>
<td>{Lang::moneyFormat($ds['balance'])}</td>
<td align="center">
{if $ds['phonenumber']}
<a href="tel:{$ds['phonenumber']}" class="btn btn-default btn-xs"
title="{$ds['phonenumber']}"><i class="glyphicon glyphicon-earphone"></i></a>
{/if}
{if $ds['email']}
<a href="mailto:{$ds['email']}" class="btn btn-default btn-xs"
title="{$ds['email']}"><i class="glyphicon glyphicon-envelope"></i></a>
{/if}
{if $ds['coordinates']}
<a href="https://www.google.com/maps/dir//{$ds['coordinates']}/" target="_blank"
class="btn btn-default btn-xs" title="{$ds['coordinates']}"><i
class="glyphicon glyphicon-map-marker"></i></a>
{/if}
</td>
<td align="center" api-get-text="{Text::url('autoload/plan_is_active/')}{$ds['id']}">
<span class="label label-default">&bull;</span>
</td>
<td>{$ds['service_type']}</td>
<td>
{$ds['pppoe_username']}
{if !empty($ds['pppoe_username']) && !empty($ds['pppoe_ip'])}:{/if}
{$ds['pppoe_ip']}
</td>
<td>{Lang::T($ds['status'])}</td>
<td>{Lang::dateTimeFormat($ds['created_at'])}</td>
<td align="center">
<a href="{Text::url('customers/view/')}{$ds['id']}" id="{$ds['id']}"
style="margin: 0px; color:black"
class="btn btn-success btn-xs">&nbsp;&nbsp;{Lang::T('View')}&nbsp;&nbsp;</a>
<a href="{Text::url('customers/edit/', $ds['id'], '&token=', $csrf_token)}"
id="{$ds['id']}" style="margin: 0px; color:black"
class="btn btn-info btn-xs">&nbsp;&nbsp;{Lang::T('Edit')}&nbsp;&nbsp;</a>
<a href="{Text::url('customers/sync/', $ds['id'], '&token=', $csrf_token)}"
id="{$ds['id']}" style="margin: 5px; color:black"
class="btn btn-success btn-xs">&nbsp;&nbsp;{Lang::T('Sync')}&nbsp;&nbsp;</a>
<a href="{Text::url('plan/recharge/', $ds['id'], '&token=', $csrf_token)}"
id="{$ds['id']}" style="margin: 0px;"
class="btn btn-primary btn-xs">{Lang::T('Recharge')}</a>
</td>
</tr>
{/foreach}
</tbody>
</table>
<div class="row" style="padding: 5px">
<div class="col-lg-3 col-lg-offset-9">
<div class="btn-group btn-group-justified" role="group">
<!-- <div class="btn-group" role="group">
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
<button id="deleteSelectedTokens" class="btn btn-danger">{Lang::T('Delete
Selected')}</button>
{/if}
</div> -->
<div class="btn-group" role="group">
<button id="sendMessageToSelected" class="btn btn-success">{Lang::T('Send
Message')}</button>
</div>
</div>
</div>
</div>
</div>
{include file="pagination.tpl"}
</div>
</div>
</div>
</div>
{include file="sections/footer.tpl"}
<!-- Modal for Sending Messages -->
<div id="sendMessageModal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="sendMessageModalLabel"
aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="sendMessageModalLabel">{Lang::T('Send Message')}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<select 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>
<textarea id="messageContent" class="form-control" rows="4"
placeholder="{Lang::T('Enter your message here...')}"></textarea>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">{Lang::T('Close')}</button>
<button type="button" id="sendMessageButton" class="btn btn-primary">{Lang::T('Send Message')}</button>
</div>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
// Select or deselect all checkboxes
document.getElementById('select-all').addEventListener('change', function () {
var checkboxes = document.querySelectorAll('input[name="customer_ids[]"]');
for (var checkbox of checkboxes) {
checkbox.checked = this.checked;
}
});
$(document).ready(function () {
let selectedCustomerIds = [];
// Collect selected customer IDs when the button is clicked
$('#sendMessageToSelected').on('click', function () {
selectedCustomerIds = $('input[name="customer_ids[]"]:checked').map(function () {
return $(this).val();
}).get();
if (selectedCustomerIds.length === 0) {
Swal.fire({
title: 'Error!',
text: 'Please select at least one customer to send a message.',
icon: 'error',
confirmButtonText: 'OK'
});
return;
}
// Open the modal
$('#sendMessageModal').modal('show');
});
// Handle sending the message
$('#sendMessageButton').on('click', function () {
const message = $('#messageContent').val().trim();
const messageType = $('#messageType').val();
if (!message) {
Swal.fire({
title: 'Error!',
text: 'Please enter a message to send.',
icon: 'error',
confirmButtonText: 'OK'
});
return;
}
// Disable the button and show loading text
$(this).prop('disabled', true).text('Sending...');
$.ajax({
url: '?_route=message/send_bulk_selected',
method: 'POST',
data: {
customer_ids: selectedCustomerIds,
message_type: messageType,
message: message
},
dataType: 'json',
success: function (response) {
// Handle success response
if (response.status === 'success') {
Swal.fire({
title: 'Success!',
text: 'Message sent successfully.',
icon: 'success',
confirmButtonText: 'OK'
});
} else {
Swal.fire({
title: 'Error!',
text: 'Error sending message: ' + response.message,
icon: 'error',
confirmButtonText: 'OK'
});
}
$('#sendMessageModal').modal('hide');
$('#messageContent').val(''); // Clear the message content
},
error: function () {
Swal.fire({
title: 'Error!',
text: 'Failed to send the message. Please try again.',
icon: 'error',
confirmButtonText: 'OK'
});
},
complete: function () {
// Re-enable the button and reset text
$('#sendMessageButton').prop('disabled', false).text('{Lang::T('Send Message')}');
}
});
});
});
$(document).ready(function () {
$('#sendMessageModal').on('show.bs.modal', function () {
$(this).attr('inert', 'true');
});
$('#sendMessageModal').on('shown.bs.modal', function () {
$('#messageContent').focus();
$(this).removeAttr('inert');
});
$('#sendMessageModal').on('hidden.bs.modal', function () {
// $('#button').focus();
});
});
</script>
{include file = "sections/footer.tpl" }

View File

@ -1,180 +1,219 @@
{include file="sections/header.tpl"}
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.11.3/css/jquery.dataTables.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<div class="row">
<div class="col-sm-12 col-md-12">
{if $page>0 && $totalCustomers>0}
<div class="alert alert-info" role="alert"><span class="loading"></span> {Lang::T("Sending message in progress. Don't close this page.")}</div>
{/if}
<div class="panel panel-primary panel-hovered panel-stacked mb30 {if $page>0 && $totalCustomers >0}hidden{/if}">
<div class="panel-heading">{Lang::T('Send Bulk Message')}</div>
<div class="panel-body">
<form class="form-horizontal" method="get" role="form" id="bulkMessageForm" action="">
<input type="hidden" name="page" value="{if $page>0 && $totalCustomers==0}-1{else}{$page}{/if}">
<div class="form-group">
<label class="col-md-2 control-label">{Lang::T('Group')}</label>
<div class="col-md-6">
<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>
</select>
</div>
</div>
<div class="form-group">
<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" {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">
<label class="col-md-2 control-label">{Lang::T('Message per time')}</label>
<div class="col-md-6">
<select class="form-control" name="batch" id="batch">
<option value="5" {if $batch == '5'}selected{/if}>{Lang::T('5 Messages')}</option>
<option value="10" {if $batch == '10'}selected{/if}>{Lang::T('10 Messages')}</option>
<option value="15" {if $batch == '15'}selected{/if}>{Lang::T('15 Messages')}</option>
<option value="20" {if $batch == '20'}selected{/if}>{Lang::T('20 Messages')}</option>
<option value="30" {if $batch == '30'}selected{/if}>{Lang::T('30 Messages')}</option>
<option value="40" {if $batch == '40'}selected{/if}>{Lang::T('40 Messages')}</option>
<option value="50" {if $batch == '50'}selected{/if}>{Lang::T('50 Messages')}</option>
<option value="60" {if $batch == '60'}selected{/if}>{Lang::T('60 Messages')}</option>
</select>{Lang::T('Use 20 and above if you are sending to all customers to avoid server time out')}
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">{Lang::T('Delay')}</label>
<div class="col-md-6">
<select class="form-control" name="delay" id="delay">
<option value="1" {if $delay == '1'}selected{/if}>{Lang::T('No Delay')}</option>
<option value="5" {if $delay == '5'}selected{/if}>{Lang::T('5 Seconds')}</option>
<option value="10" {if $delay == '10'}selected{/if}>{Lang::T('10 Seconds')}</option>
<option value="15" {if $delay == '15'}selected{/if}>{Lang::T('15 Seconds')}</option>
<option value="20" {if $delay == '20'}selected{/if}>{Lang::T('20 Seconds')}</option>
</select>{Lang::T('Use at least 5 secs if you are sending to all customers to avoid being banned by your message provider')}
</div>
</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>
<input name="test" type="checkbox">
{Lang::T('Testing [if checked no real message is sent]')}
</div>
<p class="help-block col-md-4">
{Lang::T('Use placeholders:')}
<br>
<b>[[name]]</b> - {Lang::T('Customer Name')}
<br>
<b>[[user_name]]</b> - {Lang::T('Customer Username')}
<br>
<b>[[phone]]</b> - {Lang::T('Customer Phone')}
<br>
<b>[[company_name]]</b> - {Lang::T('Your Company Name')}
</p>
</div>
<div class="form-group">
<div class="col-lg-offset-2 col-lg-10">
{if $page >= 0}
<button class="btn btn-success" id="submit" type="submit" name=send value=now>
{Lang::T('Send Message')}</button>
{else}
<button class="btn btn-success"
onclick="return ask(this, 'Continue the process of sending mass messages?')"
type="submit" name=send value=now>
{Lang::T('Send Message')}</button>
{/if}
<a href="{Text::url('dashboard')}" class="btn btn-default">{Lang::T('Cancel')}</a>
</div>
</div>
</form>
</div>
</div>
</div>
<div class="col-sm-12 col-md-12">
<div id="status" class="mb-3"></div>
<div class="panel panel-primary panel-hovered panel-stacked mb30 {if $page>0 && $totalCustomers >0}hidden{/if}">
<div class="panel-heading">{Lang::T('Send Bulk Message')}</div>
<div class="panel-body">
<form class="form-horizontal" method="get" role="form" id="bulkMessageForm" action="">
<input type="hidden" name="page" value="{if $page>0 && $totalCustomers==0}-1{else}{$page}{/if}">
<div class="form-group">
<label class="col-md-2 control-label">{Lang::T('Router')}</label>
<div class="col-md-6">
<select class="form-control select2" name="router" id="router">
<option value="">{Lang::T('All Routers')}</option>
{foreach $routers as $router}
<option value="{$router['id']}">{$router['name']}</option>
{/foreach}
</select>
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">{Lang::T('Group')}</label>
<div class="col-md-6">
<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>
</select>
</div>
</div>
<div class="form-group">
<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" {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">
<label class="col-md-2 control-label">{Lang::T('Message per time')}</label>
<div class="col-md-6">
<select class="form-control" name="batch" id="batch">
<option value="5" {if $batch=='5' }selected{/if}>{Lang::T('5 Messages')}</option>
<option value="10" {if $batch=='10' }selected{/if}>{Lang::T('10 Messages')}</option>
<option value="15" {if $batch=='15' }selected{/if}>{Lang::T('15 Messages')}</option>
<option value="20" {if $batch=='20' }selected{/if}>{Lang::T('20 Messages')}</option>
<option value="30" {if $batch=='30' }selected{/if}>{Lang::T('30 Messages')}</option>
<option value="40" {if $batch=='40' }selected{/if}>{Lang::T('40 Messages')}</option>
<option value="50" {if $batch=='50' }selected{/if}>{Lang::T('50 Messages')}</option>
<option value="60" {if $batch=='60' }selected{/if}>{Lang::T('60 Messages')}</option>
</select>
{Lang::T('Use 20 and above if you are sending to all customers to avoid server time out')}
</div>
</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>
<input name="test" id="test" type="checkbox">
{Lang::T('Testing [if checked no real message is sent]')}
</div>
<p class="help-block col-md-4">
{Lang::T('Use placeholders:')}
<br>
<b>[[name]]</b> - {Lang::T('Customer Name')}
<br>
<b>[[user_name]]</b> - {Lang::T('Customer Username')}
<br>
<b>[[phone]]</b> - {Lang::T('Customer Phone')}
<br>
<b>[[company_name]]</b> - {Lang::T('Your Company Name')}
</p>
</div>
<div class="form-group">
<div class="col-lg-offset-2 col-lg-10">
<button type="button" id="startBulk" class="btn btn-primary">Start Bulk Messaging</button>
<a href="{Text::url('dashboard')}" class="btn btn-default">{Lang::T('Cancel')}</a>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
{if $batchStatus}
<p><span class="label label-success">{Lang::T('Total SMS Sent')}: {$totalSMSSent}</span> <span
class="label label-danger">{Lang::T('Total SMS
Failed')}: {$totalSMSFailed}</span> <span class="label label-success">{Lang::T('Total WhatsApp Sent')}:
{$totalWhatsappSent}</span> <span class="label label-danger">{Lang::T('Total WhatsApp Failed')}:
{$totalWhatsappFailed}</span></p>
{/if}
<div class="box">
<div class="box-header">
<h3 class="box-title">{Lang::T('Message Results')}</h3>
</div>
<!-- /.box-header -->
<div class="box-body">
<table id="messageResultsTable" class="table table-bordered table-striped table-condensed">
<thead>
<tr>
<th>{Lang::T('Name')}</th>
<th>{Lang::T('Phone')}</th>
<th>{Lang::T('Message')}</th>
<th>{Lang::T('Status')}</th>
</tr>
</thead>
<tbody>
{foreach $batchStatus as $customer}
<tr>
<td>{$customer.name}</td>
<td>{$customer.phone}</td>
<td>{$customer.message}</td>
<td>{$customer.status}</td>
</tr>
{/foreach}
</tbody>
</table>
</div>
<!-- /.box-body -->
<!-- Add a Table for Sent History -->
<div class="panel panel-default">
<div class="panel-heading">{Lang::T('Message Sending History')}</div>
<div class="panel-body">
<div id="status"></div>
<table class="table table-bordered" id="historyTable">
<thead>
<tr>
<th>{Lang::T('Customer')}</th>
<th>{Lang::T('Phone')}</th>
<th>{Lang::T('Status')}</th>
<th>{Lang::T('Message')}</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
<!-- /.box -->
<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>
{literal}
<script>
var $j = jQuery.noConflict();
let page = 0;
let totalSent = 0;
let totalFailed = 0;
let hasMore = true;
$j(document).ready(function() {
$j('#messageResultsTable').DataTable();
});
// Initialize DataTable
let historyTable = $('#historyTable').DataTable({
paging: true,
searching: true,
ordering: true,
info: true,
autoWidth: false,
responsive: true
});
{if $page>0 && $totalCustomers >0}
setTimeout(() => {
document.getElementById('submit').click();
}, {$delay}000);
{/if}
{if $page>0 && $totalCustomers==0}
Swal.fire({
icon: 'success',
title: 'Bulk Send Done',
position: 'top-end',
showConfirmButton: false,
timer: 5000,
timerProgressBar: true,
didOpen: (toast) => {
toast.addEventListener('mouseenter', Swal.stopTimer)
toast.addEventListener('mouseleave', Swal.resumeTimer)
}
});
{/if}
function sendBatch() {
if (!hasMore) return;
$.ajax({
url: '?_route=message/send_bulk_ajax',
method: 'POST',
data: {
group: $('#group').val(),
message: $('#message').val(),
via: $('#via').val(),
batch: $('#batch').val(),
router: $('#router').val() || '',
page: page,
test: $('#test').is(':checked') ? 'on' : 'off'
},
dataType: 'json',
beforeSend: function () {
$('#status').html(`
<div class="alert alert-info">
<i class="fas fa-spinner fa-spin"></i> Sending batch ${page + 1}...
</div>
`);
},
success: function (response) {
console.log("Response received:", response);
if (response && response.status === 'success') {
totalSent += response.totalSent || 0;
totalFailed += response.totalFailed || 0;
page = response.page || 0;
hasMore = response.hasMore || false;
$('#status').html(`
<div class="alert alert-success">
<i class="fas fa-check-circle"></i> Batch ${page} sent! (Total Sent: ${totalSent}, Failed: ${totalFailed})
</div>
`);
(response.batchStatus || []).forEach(msg => {
let statusClass = msg.status.includes('Failed') ? 'danger' : 'success';
historyTable.row.add([
msg.name,
msg.phone,
`<span class="text-${statusClass}">${msg.status}</span>`,
msg.message || 'No message'
]).draw(false); // Add row without redrawing the table
});
if (hasMore) {
sendBatch();
} else {
$('#status').html(`
<div class="alert alert-success">
<i class="fas fa-check-circle"></i> All batches sent! Total Sent: ${totalSent}, Failed: ${totalFailed}
</div>
`);
}
} else {
console.error("Unexpected response format:", response);
$('#status').html(`
<div class="alert alert-danger">
<i class="fas fa-exclamation-circle"></i> Error: Unexpected response format.
</div>
`);
}
},
error: function () {
$('#status').html(`
<div class="alert alert-danger">
<i class="fas fa-exclamation-circle"></i> Error: Failed to send batch ${page + 1}.
</div>
`);
}
});
}
// Start sending on button click
$('#startBulk').on('click', function () {
page = 0;
totalSent = 0;
totalFailed = 0;
hasMore = true;
$('#status').html('<div class="alert alert-info"><i class="fas fa-spinner fa-spin"></i> Starting bulk message sending...</div>');
historyTable.clear().draw(); // Clear history table before starting
sendBatch();
});
</script>
{/literal}
{include file="sections/footer.tpl"}