feat: enhance message sending interface with channel selection and validation

This commit is contained in:
Focuslinkstech 2025-04-24 16:44:06 +01:00
parent fb558f4ac6
commit e24c36cd90
3 changed files with 232 additions and 149 deletions

View file

@ -59,16 +59,25 @@ EOT;
$id_customer = $_POST['id_customer'] ?? '';
$message = $_POST['message'] ?? '';
$via = $_POST['via'] ?? '';
$subject = $_POST['subject'] ?? '';
$channels = ['email', 'sms', 'wa', 'inbox'];
// 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)) {
r2(getUrl('message/send'), 'e', Lang::T('Please select a customer'));
}
if (empty($id_customer) || empty($message) || empty($via)) {
r2(getUrl('message/send'), 'e', Lang::T('Customer, Message, and Channel are required'));
if (empty($subject) && (isset($_POST['email']) || isset($_POST['inbox']))) {
r2(getUrl('message/send'), 'e', Lang::T('Subject is required'));
}
if (empty($message)) {
r2(getUrl('message/send'), 'e', Lang::T('Message is required'));
}
if (count(array_intersect_key(array_flip($channels), $_POST)) === 0) {
r2(getUrl('message/send'), 'e', Lang::T('Please select at least one channel type'));
}
$customer = ORM::for_table('tbl_customers')->find_one($id_customer);
@ -107,19 +116,19 @@ EOT;
// Send the message through the selected channels
$smsSent = $waSent = $emailSent = $inboxSent = false;
if ($via === 'sms' || $via === 'both' || $via === 'all') {
if (isset($_POST['sms'])) {
$smsSent = Message::sendSMS($customer['phonenumber'], $currentMessage);
}
if ($via === 'wa' || $via === 'both' || $via === 'all') {
if (isset($_POST['wa'])) {
$waSent = Message::sendWhatsapp($customer['phonenumber'], $currentMessage);
}
if ($via === 'email' || $via === 'all') {
if (isset($_POST['email'])) {
$emailSent = Message::sendEmail($customer['email'], $currentSubject, $currentMessage);
}
if ($via === 'inbox' || $via === 'all') {
if (isset($_POST['inbox'])) {
$inboxSent = Message::addToInbox($customer['id'], $currentSubject, $currentMessage, 'Admin');
}
@ -152,20 +161,31 @@ EOT;
// Get request parameters
$group = $_REQUEST['group'] ?? '';
$message = $_REQUEST['message'] ?? '';
$via = $_REQUEST['via'] ?? '';
$batch = $_REQUEST['batch'] ?? 100;
$page = $_REQUEST['page'] ?? 0;
$router = $_REQUEST['router'] ?? null;
$test = isset($_REQUEST['test']) && $_REQUEST['test'] === 'on';
$service = $_REQUEST['service'] ?? '';
$subject = $_REQUEST['subject'] ?? '';
$channels = ['email', 'sms', 'wa', 'inbox'];
$selectedChannels = [];
if (empty($group) || empty($message) || empty($via) || empty($service)) {
foreach ($channels as $channel) {
if (isset($_REQUEST[$channel]) && $_REQUEST[$channel] == '1') {
$selectedChannels[] = $channel;
}
}
if (empty($selectedChannels)) {
die(json_encode(['status' => 'error', 'message' => Lang::T('Please select at least one channel type')]));
}
if (empty($group) || empty($message) || empty($service)) {
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 . '.']));
if (array_intersect($selectedChannels, ['email', 'inbox']) && empty($subject)) {
die(json_encode(['status' => 'error', 'message' => LANG::T('Subject is required') . '.']));
}
// Get batch of customers based on group
@ -324,7 +344,7 @@ EOT;
$totalInboxFailed = 0;
$batchStatus = [];
//$subject = $config['CompanyName'] . ' ' . Lang::T('Notification Message');
$form = 'Admin';
$from = 'Admin';
foreach ($customers as $customer) {
$currentMessage = str_replace(
@ -353,50 +373,115 @@ EOT;
if ($test) {
$batchStatus[] = [
'name' => $customer['fullname'],
'channel' => 'Test Channel',
'sent' => $customer['phonenumber'],
'channel' => implode(', ', array_map('ucfirst', $selectedChannels)),
'status' => 'Test Mode',
'message' => $currentMessage,
'service' => $service,
'router' => $routerName,
];
} else {
if ($via === 'sms' || $via === 'both' || $via === 'all') {
if (isset($_REQUEST['sms']) && $_REQUEST['sms'] == '1') {
if (Message::sendSMS($customer['phonenumber'], $currentMessage)) {
$totalSMSSent++;
$batchStatus[] = ['name' => $customer['fullname'], 'phone' => $customer['phonenumber'], 'status' => 'SMS Sent', 'message' => $currentMessage];
$batchStatus[] = [
'name' => $customer['fullname'],
'sent' => $customer['phonenumber'],
'channel' => 'SMS',
'status' => 'SMS Sent',
'message' => $currentMessage,
'service' => $service,
'router' => $routerName,
];
} else {
$totalSMSFailed++;
$batchStatus[] = ['name' => $customer['fullname'], 'phone' => $customer['phonenumber'], 'status' => 'SMS Failed', 'message' => $currentMessage];
$batchStatus[] = [
'name' => $customer['fullname'],
'sent' => $customer['phonenumber'],
'channel' => 'SMS',
'status' => 'SMS Failed',
'message' => $currentMessage,
'service' => $service,
'router' => $routerName,
];
}
}
if ($via === 'wa' || $via == 'both' || $via === 'all') {
if (isset($_REQUEST['wa']) && $_REQUEST['wa'] == '1') {
if (Message::sendWhatsapp($customer['phonenumber'], $currentMessage)) {
$totalWhatsappSent++;
$batchStatus[] = ['name' => $customer['fullname'], 'channel' => $customer['phonenumber'], 'status' => 'WhatsApp Sent', 'message' => $currentMessage];
$batchStatus[] = [
'name' => $customer['fullname'],
'sent' => $customer['phonenumber'],
'channel' => 'WhatsApp',
'status' => 'WhatsApp Sent',
'message' => $currentMessage,
'service' => $service,
'router' => $routerName,
];
} else {
$totalWhatsappFailed++;
$batchStatus[] = ['name' => $customer['fullname'], 'channel' => $customer['phonenumber'], 'status' => 'WhatsApp Failed', 'message' => $currentMessage];
$batchStatus[] = [
'name' => $customer['fullname'],
'sent' => $customer['phonenumber'],
'channel' => 'WhatsApp',
'status' => 'WhatsApp Failed',
'message' => $currentMessage,
'service' => $service,
'router' => $routerName,
];
}
}
if ($via === 'email' || $via === 'all') {
if (isset($_REQUEST['email']) && $_REQUEST['email'] == '1') {
if (Message::sendEmail($customer['email'], $currentSubject, $currentMessage)) {
$totalEmailSent++;
$batchStatus[] = ['name' => $customer['fullname'], 'channel' => $customer['email'], 'status' => 'Email Sent', 'message' => $currentMessage];
$batchStatus[] = [
'name' => $customer['fullname'],
'sent' => $customer['email'],
'channel' => 'Email',
'status' => 'Email Sent',
'message' => $currentMessage,
'service' => $service,
'router' => $routerName,
];
} else {
$totalEmailFailed++;
$batchStatus[] = ['name' => $customer['fullname'], 'channel' => $customer['email'], 'status' => 'Email Failed', 'message' => $currentMessage];
$batchStatus[] = [
'name' => $customer['fullname'],
'sent' => $customer['email'],
'channel' => 'Email',
'status' => 'Email Failed',
'message' => $currentMessage,
'service' => $service,
'router' => $routerName,
];
}
}
if ($via === 'inbox' || $via === 'all') {
if (Message::addToInbox($customer['customer_id'], $currentSubject, $currentMessage, $form)) {
if (isset($_REQUEST['inbox']) && $_REQUEST['inbox'] == '1') {
if (Message::addToInbox($customer['customer_id'], $currentSubject, $currentMessage, $from)) {
$totalInboxSent++;
$batchStatus[] = ['name' => $customer['fullname'], 'channel' => 'Inbox', 'status' => 'Inbox Message Sent', 'message' => $currentMessage];
$batchStatus[] = [
'name' => $customer['fullname'],
'sent' => $customer['username'],
'channel' => 'Inbox',
'status' => 'Inbox Message Sent',
'message' => $currentMessage,
'service' => $service,
'router' => $routerName,
];
} else {
$totalInboxFailed++;
$batchStatus[] = ['name' => $customer['fullname'], 'channel' => 'Inbox', 'status' => 'Inbox Message Failed', 'message' => $currentMessage];
$batchStatus[] = [
'name' => $customer['fullname'],
'sent' => $customer['username'],
'channel' => 'Inbox',
'status' => 'Inbox Message Failed',
'message' => $currentMessage,
'service' => $service,
'router' => $routerName,
];
}
}
}
@ -414,8 +499,6 @@ EOT;
'totalSent' => $totalSMSSent + $totalWhatsappSent + $totalEmailSent + $totalInboxSent,
'totalFailed' => $totalSMSFailed + $totalWhatsappFailed + $totalEmailFailed + $totalInboxFailed,
'hasMore' => $hasMore,
'service' => $service,
'router' => $routerName,
]);
break;
@ -442,7 +525,7 @@ EOT;
// Prepare to send messages
$sentCount = 0;
$failedCount = 0;
$form = 'Admin';
$from = 'Admin';
foreach ($customerIds as $customerId) {
$customer = ORM::for_table('tbl_customers')->where('id', $customerId)->find_one();
@ -458,7 +541,7 @@ EOT;
$messageSent = Message::sendWhatsapp($customer['phonenumber'], $message);
}
if (!$messageSent && ($via === 'inbox' || $via === 'all')) {
Message::addToInbox($customer['id'], $subject, $message, $form);
Message::addToInbox($customer['id'], $subject, $message, $from);
$messageSent = true;
}
if (!$messageSent && ($via === 'email' || $via === 'all')) {

View file

@ -49,20 +49,6 @@
</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="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">
<label class="col-md-2 control-label">{Lang::T('Message per time')}</label>
<div class="col-md-6">
@ -76,17 +62,34 @@
<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>
<p class="help-block col-md-4">
<small>
{Lang::T('Use 20 and above if you are sending to many customers')}
</small>
</p>
</div>
<div class="form-group" id="subject-content">
<div class="form-group" id="via">
<label class="col-md-2 control-label">{Lang::T('Channel')}</label>
<label class="col-md-1 control-label"><input type="checkbox" id="sms" name="sms" value="1">
{Lang::T('SMS')}</label>
<label class="col-md-1 control-label"><input type="checkbox" id="wa" name="wa" value="1">
{Lang::T('WA')}</label>
<label class="col-md-1 control-label"><input type="checkbox" id="email" name="email" value="1">
{Lang::T('Email')}</label>
<label class="col-md-1 control-label"><input type="checkbox" id="inbox" name="inbox" value="1">
{Lang::T('Inbox')}</label>
</div>
<div class="form-group" id="subject" style="display: none;">
<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=""
<input type="text" class="form-control" name="subject" id="subjectContent" 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')}.
<small>
{Lang::T('You can also use the below placeholders here too')}.
</small>
</p>
</div>
<div class="form-group">
@ -95,18 +98,20 @@
<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]')}
<small> {Lang::T('Testing [if checked no real message is sent]')}</small>
</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')}
<small>
{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')}
</small>
</p>
</div>
<div class="form-group">
@ -131,6 +136,7 @@
<thead>
<tr>
<th>{Lang::T('Customer')}</th>
<th>{Lang::T('Sent To')}</th>
<th>{Lang::T('Channel')}</th>
<th>{Lang::T('Status')}</th>
<th>{Lang::T('Message')}</th>
@ -146,31 +152,25 @@
<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');
document.addEventListener('DOMContentLoaded', function () {
const emailCheckbox = document.getElementById('email');
const inboxCheckbox = document.getElementById('inbox');
const subjectDiv = document.getElementById('subject');
const subjectInput = document.getElementById('subjectContent');
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;
function toggleSubjectField() {
if (emailCheckbox.checked || inboxCheckbox.checked) {
subjectDiv.style.display = 'block';
subjectInput.required = true;
} else {
subjectDiv.style.display = 'none';
subjectInput.required = false;
subjectInput.value = '';
}
}
emailCheckbox.addEventListener('change', toggleSubjectField);
inboxCheckbox.addEventListener('change', toggleSubjectField);
});
</script>
{literal}
@ -199,13 +199,16 @@
data: {
group: $('#group').val(),
message: $('#message').val(),
via: $('#via').val(),
sms: $('#sms').is(':checked') ? '1' : '0',
wa: $('#wa').is(':checked') ? '1' : '0',
email: $('#email').is(':checked') ? '1' : '0',
inbox: $('#inbox').is(':checked') ? '1' : '0',
batch: $('#batch').val(),
router: $('#router').val() || '',
page: page,
test: $('#test').is(':checked') ? 'on' : 'off',
service: $('#service').val(),
subject: $('#subject').val(),
subject: $('#subjectContent').val(),
},
dataType: 'json',
beforeSend: function () {
@ -233,10 +236,11 @@
(response.batchStatus || []).forEach(msg => {
let statusClass = msg.status.includes('Failed') ? 'danger' : 'success';
historyTable.row.add([
msg.name,
msg.channel,
msg.name ? msg.name : 'Unknown Customer',
msg.sent ? msg.sent : 'Unknown Recipient',
msg.channel ? msg.channel : 'Unknown Channel',
`<span class="text-${statusClass}">${msg.status}</span>`,
msg.message || 'No message',
msg.message ? msg.message : 'No Message',
msg.router ? msg.router : 'All Router',
msg.service == 'all' ? 'All Service' : (msg.service || 'No Service')
]).draw(false); // Add row without redrawing the table

View file

@ -13,36 +13,35 @@
name="id_customer" style="width: 100%"
data-placeholder="{Lang::T('Select a customer')}...">
{if $cust}
<option value="{$cust['id']}">{$cust['username']} &bull; {$cust['fullname']} &bull;
{$cust['email']}</option>
<option value="{$cust['id']}">{$cust['username']} &bull; {$cust['fullname']} &bull;
{$cust['email']}</option>
{/if}
</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="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 class="form-group" id="via">
<label class="col-md-2 control-label">{Lang::T('Channel')}</label>
<label class="col-md-1 control-label"><input type="checkbox" id="sms" name="sms" value="1">
{Lang::T('SMS')}</label>
<label class="col-md-1 control-label"><input type="checkbox" id="wa" name="wa" value="1">
{Lang::T('WA')}</label>
<label class="col-md-1 control-label"><input type="checkbox" id="email" name="email" value="1">
{Lang::T('Email')}</label>
<label class="col-md-1 control-label"><input type="checkbox" id="inbox" name="inbox" value="1">
{Lang::T('Inbox')}</label>
</div>
<div class="form-group" id="subject" style="display: none;">
<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">
<small>
{Lang::T('You can also use the below placeholders here too')}.
</small>
</p>
</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>
<div class="col-md-6">
@ -50,18 +49,21 @@
placeholder="{Lang::T('Compose your message...')}" rows="5"></textarea>
</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')}
<br>
<b>[[payment_link]]</b> - <a href="{Text::url('docs')}/#Reminder%20with%20payment%20link"
target="_blank">{Lang::T('Read documentation')}</a>.
<small>
{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')}
<br>
<b>[[payment_link]]</b> - <a
href="{Text::url('docs')}/#Reminder%20with%20payment%20link"
target="_blank">{Lang::T('Read documentation')}</a>.
</small>
</p>
</div>
@ -79,32 +81,26 @@
</div>
</div>
<script>
document.getElementById('via').addEventListener('change', function () {
const via = this.value;
const subject = document.getElementById('subject');
const subjectField = document.getElementById('subject-content');
document.addEventListener('DOMContentLoaded', function () {
const emailCheckbox = document.getElementById('email');
const inboxCheckbox = document.getElementById('inbox');
const subjectDiv = document.getElementById('subject');
const subjectInput = document.getElementById('subject-content');
subject.style.display = (via === 'all' || via === 'email' || via === 'inbox') ? 'block' : 'none';
function toggleSubjectField() {
if (emailCheckbox.checked || inboxCheckbox.checked) {
subjectDiv.style.display = 'block';
subjectInput.required = true;
} else {
subjectDiv.style.display = 'none';
subjectInput.required = false;
subjectInput.value = '';
}
}
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;
}
});
emailCheckbox.addEventListener('change', toggleSubjectField);
inboxCheckbox.addEventListener('change', toggleSubjectField);
});
</script>
{include file="sections/footer.tpl"}