nhance/app/Views/retail_policy_list.php

1092 lines
42 KiB
PHP

<style>
.table th,
.table td {
padding: 8px;
}
table.dataTable tbody td {
padding: 4px 4px !important;
}
#retail_policy_list {
margin-top: 18px;
}
#retail_policy_list .card-body {
padding: 12px 16px 8px;
}
#retail-policy-table_wrapper > .row:first-child {
margin-top: 4px;
margin-bottom: 10px;
}
#retail-policy-table .renewal-status-badge {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 72px;
height: 22px;
padding: 0 8px;
font-size: 11px;
font-weight: 600;
border-radius: 12px;
line-height: 1;
}
#retail-policy-table_wrapper .row {
margin-left: 0;
margin-right: 0;
margin-bottom: 6px;
}
#retail-policy-table_wrapper .row:last-child {
margin-bottom: 0;
}
#retail-policy-table_wrapper .dataTables_filter,
#retail-policy-table_wrapper .dt-buttons {
margin-bottom: 0;
}
#retail-policy-table_wrapper .dataTables_filter label {
margin-bottom: 0;
}
#retail-policy-table_wrapper .dataTables_info,
#retail-policy-table_wrapper .dataTables_length label,
#retail-policy-table_wrapper .dataTables_paginate {
margin-bottom: 0;
padding-top: 0;
font-size: 13px;
}
#retail-policy-table_wrapper .dataTables_length select {
margin: 0 6px;
padding: 2px 6px;
height: auto;
}
#retail_reminder_config_modal .modal-dialog {
width: 90%;
max-width: 980px;
}
#retail_reminder_template_modal .modal-dialog {
width: 95%;
max-width: 1200px;
}
#retail_reminder_config_modal .modal-header,
#retail_reminder_template_modal .modal-header {
display: flex;
align-items: center;
justify-content: space-between;
}
#retail_reminder_config_modal .modal-title,
#retail_reminder_template_modal .modal-title {
margin: 0;
}
#retail_reminder_template_editor_container {
height: calc(100vh - 340px);
max-height: 460px;
min-height: 280px;
border: 1px solid #e9ecef;
border-radius: 8px;
overflow: hidden;
background: #fff;
}
.retail-unlayer-editor-wrap {
position: relative;
}
.retail-unlayer-branding-mask {
position: absolute;
right: 1px;
bottom: 1px;
width: 230px;
height: 36px;
background: #f5f5f5;
z-index: 25;
pointer-events: none;
}
.retail-template-placeholder-select {
width: 100%;
max-width: none;
}
#retail_reminder_template_modal .retail-template-top-row {
align-items: flex-start;
margin-bottom: 0.35rem;
}
#retail_reminder_template_modal .retail-template-top-row .form-group {
margin-bottom: 0;
}
#retail_reminder_template_modal .retail-template-top-row .form-group > label {
display: block;
margin-bottom: 0.55rem;
font-weight: 500;
line-height: 1.2;
}
#retail_reminder_template_modal .retail-template-top-row .form-control {
height: calc(1.5em + 0.75rem + 2px);
}
.retail-placeholder-hint {
font-size: 12px;
color: #6c757d;
margin-top: 6px;
}
.retail-date-field-options {
display: flex;
flex-wrap: wrap;
gap: 0.75rem 1rem;
padding-top: 0.35rem;
}
.retail-date-field-options .custom-control {
margin-bottom: 0;
}
.retail-date-field-options .custom-control-label {
cursor: pointer;
}
.retail-config-switch-row .form-group {
margin-bottom: 0;
}
#retail_reminder_config_modal .modal-body {
padding: 1.5rem 1.75rem !important;
}
#retail_reminder_config_modal .modal-body > .form-row {
margin-left: 0;
margin-right: 0;
margin-bottom: 1.35rem;
}
#retail_reminder_config_modal .modal-body > .form-row:last-child {
margin-bottom: 0;
}
#retail_reminder_config_modal .form-group {
margin-bottom: 0;
}
#retail_reminder_config_modal .form-group > label {
display: block;
margin-bottom: 0.55rem;
font-weight: 500;
}
#retail_reminder_config_modal .retail-config-switch-row {
margin-bottom: 1.5rem !important;
padding-bottom: 0.35rem;
}
#retail_reminder_config_modal .custom-control.custom-switch {
min-height: 1.75rem;
padding-top: 0.15rem;
}
#retail_reminder_config_modal .modal-footer,
#retail_reminder_template_modal .modal-footer {
padding: 1rem 1.75rem !important;
}
</style>
<div class="row" id="retail_policy_list">
<div class="col-12">
<div class="card">
<div class="card-body">
<div class="table-responsive">
<table data-custom-table-css="table" class="table table-hover mb-0 nowrap w-100 table-centered m-0" cellspacing="0" id="retail-policy-table">
<thead class="bg-light">
<tr>
<th class="font-weight-medium">S.No&nbsp;</th>
<th class="font-weight-medium">Policy No&nbsp;</th>
<th class="font-weight-medium">Policy Type&nbsp;</th>
<th class="font-weight-medium">Client Name&nbsp;</th>
<th class="font-weight-medium">Mobile&nbsp;</th>
<th class="font-weight-medium">Email&nbsp;</th>
<th class="font-weight-medium">Renewal Date&nbsp;</th>
<th class="font-weight-medium">Policy Period&nbsp;</th>
<th class="font-weight-medium">Renewal Status&nbsp;</th>
<th class="font-weight-medium">Action&nbsp;</th>
</tr>
</thead>
<tbody class="font-12"></tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- Mail Config Modal -->
<div class="modal fade" id="retail_reminder_config_modal" tabindex="-1" role="dialog" aria-labelledby="retailReminderConfigLabel" aria-hidden="true" aria-modal="true" data-backdrop="static">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header d-flex align-items-center justify-content-between">
<h4 class="modal-title" id="retailReminderConfigLabel">Retail Reminder Mail Config</h4>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
</div>
<form id="retail_reminder_config_form">
<div class="modal-body">
<div class="form-row retail-config-switch-row">
<div class="form-group col-md-3 col-sm-6">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" id="cfg_enabled" name="enabled" value="1">
<label class="custom-control-label" for="cfg_enabled">Enabled</label>
</div>
</div>
<div class="form-group col-md-3 col-sm-6">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" id="cfg_daily" name="daily" value="1">
<label class="custom-control-label" for="cfg_daily">Run Daily</label>
</div>
</div>
<div class="form-group col-md-3 col-sm-6">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" id="cfg_includeClientEmail" name="includeClientEmail" value="1">
<label class="custom-control-label" for="cfg_includeClientEmail">Include Client Email</label>
</div>
</div>
<!--
<div class="form-group col-md-3 col-sm-6">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" id="cfg_includeOverdue" name="includeOverdue" value="1">
<label class="custom-control-label" for="cfg_includeOverdue">Include Overdue</label>
</div>
</div>
-->
</div>
<div class="form-row">
<div class="form-group col-md-4">
<label>Date Field</label>
<div class="retail-date-field-options">
<div class="custom-control custom-radio">
<input type="radio" class="custom-control-input" id="cfg_dateField_renewal" name="dateField" value="renewal_date" checked>
<label class="custom-control-label" for="cfg_dateField_renewal">Renewal Date</label>
</div>
<div class="custom-control custom-radio">
<input type="radio" class="custom-control-input" id="cfg_dateField_policy_end" name="dateField" value="policy_end_date">
<label class="custom-control-label" for="cfg_dateField_policy_end">Policy End Date</label>
</div>
</div>
</div>
<div class="form-group col-md-8">
<label for="cfg_daysBefore">Days Before</label>
<input type="text" class="form-control" id="cfg_daysBefore" name="daysBefore" value="30" placeholder="30, 15, 7, 1, -2, -5, -10">
<small class="form-text text-muted">Comma-separated offsets. Positive = days before due, negative = days after due.</small>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-12">
<label for="cfg_days">Weekdays (used when daily is off)</label>
<input type="text" class="form-control" id="cfg_days" name="days" placeholder="mon,tue,wed,thu,fri">
</div>
</div>
<div class="form-row">
<div class="form-group col-md-12">
<label for="cfg_toEmails">TO Emails</label>
<input type="text" class="form-control" id="cfg_toEmails" name="toEmails" placeholder="mail1@example.com,mail2@example.com">
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<label for="cfg_ccEmails">CC Emails</label>
<input type="text" class="form-control" id="cfg_ccEmails" name="ccEmails">
</div>
<div class="form-group col-md-6">
<label for="cfg_bccEmails">BCC Emails</label>
<input type="text" class="form-control" id="cfg_bccEmails" name="bccEmails">
</div>
</div>
<div class="form-row">
<div class="form-group col-md-12">
<label for="cfg_fromMail">From Mail</label>
<input type="email" class="form-control" id="cfg_fromMail" name="fromMail" value="<?= esc($retail_default_from_mail ?? '') ?>" placeholder="sender@example.com">
</div>
</div>
<input type="hidden" name="clientTypes" value="2">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-light waves-effect waves-light" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary waves-effect waves-light">Save Config</button>
</div>
</form>
</div>
</div>
</div>
<!-- Email Template Modal -->
<div class="modal fade" id="retail_reminder_template_modal" tabindex="-1" role="dialog" aria-labelledby="retailReminderTemplateLabel" aria-hidden="true" aria-modal="true" data-backdrop="static">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header d-flex align-items-center justify-content-between">
<h4 class="modal-title" id="retailReminderTemplateLabel">Retail Reminder Email Template</h4>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
</div>
<form id="retail_reminder_template_form">
<input type="hidden" name="template_name" value="retail_reminder_mail">
<div class="modal-body">
<div class="form-row retail-template-top-row">
<div class="form-group col-md-8">
<label for="tpl_subject">Subject</label>
<input type="text" class="form-control" id="tpl_subject" name="subject" placeholder="Subject">
</div>
<div class="form-group col-md-4">
<label for="tpl_placeholder">Placeholders</label>
<select id="tpl_placeholder" class="form-control retail-template-placeholder-select">
<option value="">Insert placeholder</option>
<option value="{{client_name}}">Client Name</option>
<option value="{{client_short_name}}">Client Short Name</option>
<option value="{{policy_no}}">Policy No</option>
<option value="{{policy_type}}">Policy Type</option>
<option value="{{insurer_name}}">Insurer Name</option>
<option value="{{policy_period}}">Policy Period</option>
<option value="{{reminder_date}}">Reminder Date</option>
<option value="{{days_before}}">Days Before</option>
<option value="{{client_type}}">Client Type</option>
</select>
</div>
</div>
<div class="retail-placeholder-hint">
Select a placeholder to copy it, then paste into subject or email body.
</div>
<div class="form-group mb-0 mt-3">
<label>Mail Content</label>
<div class="retail-unlayer-editor-wrap">
<div id="retail_reminder_template_editor_container"></div>
<div class="retail-unlayer-branding-mask" aria-hidden="true"></div>
</div>
</div>
<div class="form-row mt-3 mb-0 retail-test-mail-row">
<div class="form-group col-md-8 mb-0">
<label for="tpl_test_email">Test Email</label>
<input type="email" class="form-control" id="tpl_test_email" name="test_email" placeholder="Enter email to send test mail" autocomplete="off">
<small id="tpl_test_email_error" class="text-danger d-none">Please enter a valid email address.</small>
</div>
<div class="form-group col-md-4 mb-0 d-flex align-items-end">
<button type="button" class="btn btn-info waves-effect waves-light w-100" id="btn_send_test_mail">
<i class="mdi mdi-email-send-outline"></i> Send Test Mail
</button>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-light waves-effect waves-light" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary waves-effect waves-light">Submit</button>
</div>
</form>
</div>
</div>
</div>
<script>
(function () {
var listDataUrl = '<?= base_url('policy_tranction/retail-policy/list-data') ?>';
var configUrl = '<?= base_url('policy_tranction/retail-policy/config') ?>';
var templateUrl = '<?= base_url('policy_tranction/retail-policy/template') ?>';
var markRenewedUrl = '<?= base_url('policy_tranction/retail-policy/mark-renewed') ?>';
var testMailUrl = '<?= base_url('policy_tranction/retail-policy/send-test-mail') ?>';
var retailUnlayerEditor = null;
var retailPolicyTable = null;
var retailUnlayerProjectId = <?= json_encode(getenv('unlayer.projectID')) ?>;
var retailDefaultFromMail = <?= json_encode($retail_default_from_mail ?? '') ?>;
function resolveRetailFromMail(value) {
var mail = (value != null ? String(value) : '').trim();
return mail || (retailDefaultFromMail || '');
}
function boolVal(v) {
return v === true || v === 1 || v === '1' || v === 'true';
}
function listVal(v) {
if (Array.isArray(v)) {
return v.join(',');
}
return v != null ? String(v) : '';
}
function showRetailModal(modalId) {
var modalEl = document.getElementById(modalId);
if (!modalEl) {
return;
}
try {
// Same open pattern as policy_transaction_inception_form.php (moreInfoModal)
var myModal = new bootstrap.Modal(modalEl);
myModal.show();
return;
} catch (e) {
console.error('bootstrap.Modal open failed, falling back to jQuery modal:', e);
}
$('#' + modalId).modal('show');
}
function hideRetailModal(modalId) {
var modalEl = document.getElementById(modalId);
if (!modalEl) {
return;
}
try {
if (typeof bootstrap !== 'undefined' && bootstrap.Modal && typeof bootstrap.Modal.getInstance === 'function') {
var instance = bootstrap.Modal.getInstance(modalEl);
if (instance) {
instance.hide();
return;
}
}
} catch (e) {
// fall through to jQuery hide
}
$('#' + modalId).modal('hide');
}
function hideRetailLoader() {
$('.loader').fadeOut();
$('.loader-mask').delay(200).fadeOut('slow');
}
function setRetailDateField(value) {
var normalized = value === 'policy_end_date' ? 'policy_end_date' : 'renewal_date';
$('input[name="dateField"][value="' + normalized + '"]').prop('checked', true);
}
function getRetailDateField() {
var selected = $('input[name="dateField"]:checked').val();
return selected === 'policy_end_date' ? 'policy_end_date' : 'renewal_date';
}
function populateRetailConfigForm(data) {
data = data || {};
$('#cfg_enabled').prop('checked', boolVal(data.enabled));
$('#cfg_daily').prop('checked', boolVal(data.daily));
$('#cfg_includeClientEmail').prop('checked', boolVal(data.includeClientEmail));
$('#cfg_includeOverdue').prop('checked', boolVal(data.includeOverdue));
$('#cfg_daysBefore').val(listVal(data.daysBefore) || '30');
setRetailDateField(data.dateField || 'renewal_date');
$('#cfg_days').val(listVal(data.days) || 'mon,tue,wed,thu,fri');
$('#cfg_toEmails').val(listVal(data.toEmails));
$('#cfg_ccEmails').val(listVal(data.ccEmails));
$('#cfg_bccEmails').val(listVal(data.bccEmails));
$('#cfg_fromMail').val(resolveRetailFromMail(data.fromMail));
}
function openMailConfigModal() {
$('.loader, .loader-mask').fadeIn();
$.ajax({
url: configUrl,
type: 'GET',
dataType: 'json',
success: function (res) {
try {
populateRetailConfigForm((res && res.data) ? res.data : {});
showRetailModal('retail_reminder_config_modal');
} catch (e) {
console.error('Failed to open mail config modal:', e);
toastr.error('Failed to open mail config');
}
},
error: function () {
toastr.error('Failed to load mail config');
},
complete: function () {
hideRetailLoader();
}
});
}
function isRetailUnlayerLoaded() {
return typeof unlayer !== 'undefined' && unlayer !== null;
}
function waitForRetailUnlayer(callback, maxAttempts) {
var attempts = 0;
maxAttempts = maxAttempts || 20;
var timer = setInterval(function () {
attempts++;
if (isRetailUnlayerLoaded()) {
clearInterval(timer);
callback(true);
return;
}
if (attempts >= maxAttempts) {
clearInterval(timer);
callback(false);
}
}, 500);
}
function destroyRetailTemplateEditor() {
if (retailUnlayerEditor && typeof retailUnlayerEditor.destroy === 'function') {
retailUnlayerEditor.destroy();
}
retailUnlayerEditor = null;
var container = document.getElementById('retail_reminder_template_editor_container');
if (container) {
container.innerHTML = '';
}
}
function initRetailTemplateEditor(templateData, callback) {
templateData = templateData || {};
var container = document.getElementById('retail_reminder_template_editor_container');
if (!container) {
if (callback) {
callback(null);
}
return;
}
if (!document.querySelector('script[src*="unlayer"]')) {
toastr.error('Unlayer editor is not available on this page');
if (callback) {
callback(null);
}
return;
}
waitForRetailUnlayer(function (loaded) {
if (!loaded) {
toastr.error('Failed to load Unlayer editor');
if (callback) {
callback(null);
}
return;
}
try {
destroyRetailTemplateEditor();
retailUnlayerEditor = unlayer.createEditor({
id: 'retail_reminder_template_editor_container',
projectId: retailUnlayerProjectId,
displayMode: 'email',
features: {
textEditor: {
tables: true
},
preview: {
enabled: true,
deviceOptions: ['desktop', 'mobile']
},
imageEditor: {
enabled: true
}
},
appearance: {
theme: 'light',
panels: {
tools: {
dock: 'right'
}
}
},
branding: false,
tools: {
text: { enabled: true },
image: { enabled: true }
}
});
if (templateData.mail_content_json) {
try {
retailUnlayerEditor.loadDesign(JSON.parse(templateData.mail_content_json));
} catch (error) {
console.error('Failed to load saved Unlayer design', error);
}
}
if (callback) {
callback(retailUnlayerEditor);
}
setTimeout(hideRetailUnlayerBranding, 400);
} catch (error) {
console.error('Retail Unlayer init failed', error);
if (callback) {
callback(null);
}
}
});
}
function hideRetailUnlayerBranding() {
$('#retail_reminder_template_editor_container iframe').off('load.retailUnlayerBranding').on('load.retailUnlayerBranding', function () {
var $iframe = $(this);
var $parent = $iframe.parent();
if ($parent.css('position') === 'static') {
$parent.css('position', 'relative');
}
$parent.find('.retail-unlayer-branding-overlay').remove();
$parent.append(
$('<div>', {
class: 'retail-unlayer-branding-overlay',
css: {
position: 'absolute',
right: 0,
bottom: 0,
width: $iframe.outerWidth(),
height: '36px',
backgroundColor: '#f5f5f5',
zIndex: 9999,
pointerEvents: 'none'
}
})
);
});
}
function openEmailTemplateModal() {
$('.loader, .loader-mask').fadeIn();
$.ajax({
url: templateUrl,
type: 'GET',
dataType: 'json',
success: function (res) {
try {
var data = (res && res.data) ? res.data : {};
$('#tpl_subject').val(data.subject || '');
showRetailModal('retail_reminder_template_modal');
var retryCount = 0;
var initWithRetry = function () {
initRetailTemplateEditor(data, function (editorInstance) {
if (!editorInstance && retryCount < 3) {
retryCount++;
setTimeout(initWithRetry, 800);
return;
}
if (!editorInstance) {
toastr.error('Editor initialization failed. Please refresh the page.');
}
});
};
setTimeout(initWithRetry, 300);
} catch (e) {
console.error('Failed to open email template modal:', e);
toastr.error('Failed to open email template');
}
},
error: function () {
toastr.error('Failed to load email template');
},
complete: function () {
hideRetailLoader();
}
});
}
function exportRetailPolicyData(e, dt, button, config, buttonType) {
var self = this;
var oldStart = dt.page.info().start;
var oldSearch = (dt.search() || '').trim();
dt.one('preXhr', function (x, s, data) {
data.start = 0;
data.length = -1;
data.search = { value: oldSearch, regex: false };
});
dt.one('draw', function () {
$.fn.dataTable.ext.buttons[buttonType].action.call(self, e, dt, button, config);
dt.one('preXhr', function (x, s, data) {
data.start = oldStart;
data.length = dt.page.len();
data.search = { value: oldSearch, regex: false };
});
setTimeout(function () {
dt.ajax.reload(null, false);
}, 0);
});
dt.ajax.reload();
}
function initRetailPolicyDataTable() {
if (!$.fn.DataTable) {
return;
}
var $table = $('#retail-policy-table');
if (!$table.length) {
return;
}
if ($.fn.DataTable.isDataTable('#retail-policy-table')) {
$table.DataTable().destroy();
}
if (typeof nhanceListDataTableBeforeInit === 'function') {
nhanceListDataTableBeforeInit();
}
var dtOptions = {
processing: true,
serverSide: true,
ordering: false,
pageLength: 10,
lengthMenu: [[10, 25, 50, 100], [10, 25, 50, 100]],
dom: "<'row'<'col-sm-7'f><'col-sm-5 text-right'B>>" +
"<'row'<'col-sm-12'tr>>" +
"<'row align-items-center'<'col-5 text-start'i><'col-7 d-flex justify-content-end align-items-center'<'me-2'l>p>>",
ajax: {
url: listDataUrl,
type: 'POST'
},
columns: [
{ data: 0 },
{ data: 1 },
{ data: 2 },
{ data: 3 },
{ data: 4 },
{ data: 5 },
{ data: 6 },
{ data: 7 },
{ data: 8 },
{ data: 9, orderable: false }
],
buttons: [
{
text: '<i class="mdi mdi-cog-outline"></i><span class="btn-custom"> Mail Config </span>',
className: 'btn app-btn-primary mr-2',
action: function () {
openMailConfigModal();
}
},
{
text: '<i class="mdi mdi-email-edit-outline"></i><span class="btn-custom"> Email Template </span>',
className: 'btn app-btn-secondary mr-2',
action: function () {
openEmailTemplateModal();
}
},
{
extend: 'excel',
text: '<i class="mdi mdi-file-excel"></i><span class="btn-custom"> EXCEL </span>',
className: 'btn app-btn-secondary',
title: 'Retail Policy',
sheetName: 'Retail Policy',
action: function (e, dt, button, config) {
exportRetailPolicyData.call(this, e, dt, button, config, 'excelHtml5');
},
exportOptions: {
modifier: {
search: 'applied',
page: 'all'
},
columns: [0, 1, 2, 3, 4, 5, 6, 7, 8],
format: {
body: function (data, row, column, node) {
return $(node).text().replace(/\s+/g, ' ').trim();
}
}
}
}
],
language: {
search: `
<div class="datatable-search-wrapper" style="position:relative; display:inline-block;">
_INPUT_
<i class="mdi mdi-magnify datatable-search-icon"
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666;"></i>
</div>`,
searchPlaceholder: 'Search policy no, type, name, mobile, email',
emptyTable: '<div class="text-center text-muted">No retail policies found</div>',
zeroRecords: '<div class="text-center text-muted">No matching retail policies</div>'
}
};
retailPolicyTable = typeof nhanceMergeListDataTableOptions === 'function'
? $table.DataTable(nhanceMergeListDataTableOptions(dtOptions))
: $table.DataTable(dtOptions);
if (typeof nhanceListDataTableAfterInit === 'function') {
nhanceListDataTableAfterInit();
}
if (typeof nhanceListDataTableBindAdjust === 'function') {
nhanceListDataTableBindAdjust(retailPolicyTable);
}
}
$(document).ready(function () {
initRetailPolicyDataTable();
$(document).on('click', '.btn-mark-renewed', function () {
var ptId = $(this).data('id');
if (!ptId) {
return;
}
var doMarkRenewed = function () {
$('.loader, .loader-mask').fadeIn();
$.ajax({
url: markRenewedUrl,
type: 'POST',
dataType: 'json',
data: { id: ptId },
success: function (res) {
if (res && res.status) {
toastr.success(res.message || 'Policy marked as renewed');
if (retailPolicyTable) {
retailPolicyTable.ajax.reload(null, false);
}
} else {
toastr.error((res && res.message) || 'Failed to update status');
}
},
error: function () {
toastr.error('Failed to update status');
},
complete: function () {
hideRetailLoader();
}
});
};
if (typeof confirmActionSweertAlert === 'function') {
confirmActionSweertAlert(
'Mark this policy as renewed?',
'Yes, Mark Renewed',
'Cancel',
'warning'
).then(function (confirmed) {
if (confirmed) {
doMarkRenewed();
}
});
return;
}
if (confirm('Are you sure you want to mark this policy as renewed?')) {
doMarkRenewed();
}
});
$('#retail_reminder_template_modal').on('hidden.bs.modal', function () {
destroyRetailTemplateEditor();
});
$('#retail_reminder_config_form').on('submit', function (e) {
e.preventDefault();
$('.loader, .loader-mask').fadeIn();
var formData = {
enabled: $('#cfg_enabled').is(':checked') ? '1' : '0',
daily: $('#cfg_daily').is(':checked') ? '1' : '0',
includeClientEmail: $('#cfg_includeClientEmail').is(':checked') ? '1' : '0',
includeOverdue: $('#cfg_includeOverdue').is(':checked') ? '1' : '0',
daysBefore: $('#cfg_daysBefore').val(),
dateField: getRetailDateField(),
days: $('#cfg_days').val(),
toEmails: $('#cfg_toEmails').val(),
ccEmails: $('#cfg_ccEmails').val(),
bccEmails: $('#cfg_bccEmails').val(),
fromMail: $('#cfg_fromMail').val(),
clientTypes: '2'
};
$.ajax({
url: configUrl,
type: 'POST',
dataType: 'json',
data: formData,
success: function (res) {
if (res && res.status) {
toastr.success(res.message || 'Config saved');
hideRetailModal('retail_reminder_config_modal');
} else {
toastr.error((res && res.message) || 'Failed to save config');
}
},
error: function () {
toastr.error('Failed to save config');
},
complete: function () {
$('.loader').fadeOut();
$('.loader-mask').delay(200).fadeOut('slow');
}
});
});
$('#tpl_placeholder').on('change', function () {
var val = $(this).val();
if (!val) {
return;
}
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(val);
toastr.info('Placeholder copied: ' + val);
} else {
toastr.info(val);
}
$(this).val('');
});
function isValidRetailTestEmail(email) {
var emailPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
return emailPattern.test(String(email || '').trim());
}
function showRetailTestEmailError(show) {
var $error = $('#tpl_test_email_error');
var $input = $('#tpl_test_email');
if (show) {
$error.removeClass('d-none');
$input.addClass('is-invalid');
} else {
$error.addClass('d-none');
$input.removeClass('is-invalid');
}
}
$('#tpl_test_email').on('input blur', function () {
var email = String($(this).val() || '').trim();
if (email === '') {
showRetailTestEmailError(false);
return;
}
showRetailTestEmailError(!isValidRetailTestEmail(email));
});
$('#btn_send_test_mail').on('click', function () {
var testEmail = String($('#tpl_test_email').val() || '').trim();
if (!isValidRetailTestEmail(testEmail)) {
showRetailTestEmailError(true);
toastr.warning('Please enter a valid email address');
$('#tpl_test_email').focus();
return;
}
showRetailTestEmailError(false);
var subject = String($('#tpl_subject').val() || '').trim();
if (subject === '') {
toastr.warning('Please enter a subject before sending test mail');
$('#tpl_subject').focus();
return;
}
if (!retailUnlayerEditor || typeof retailUnlayerEditor.exportHtml !== 'function') {
toastr.error('Editor not ready. Please try again.');
return;
}
$('.loader, .loader-mask').fadeIn();
retailUnlayerEditor.exportHtml(function (data) {
var mailContent = (data && data.html) ? data.html : '';
if (!mailContent) {
$('.loader').fadeOut();
$('.loader-mask').delay(200).fadeOut('slow');
toastr.error('Mail content is empty. Please design the template first.');
return;
}
$.ajax({
url: testMailUrl,
type: 'POST',
dataType: 'json',
data: {
test_email: testEmail,
subject: subject,
mailContent: mailContent
},
success: function (res) {
if (res && res.status) {
toastr.success(res.message || 'Test mail sent');
} else {
toastr.error((res && res.message) || 'Failed to send test mail');
}
},
error: function (xhr) {
var message = 'Failed to send test mail';
if (xhr.responseJSON && xhr.responseJSON.message) {
message = xhr.responseJSON.message;
}
toastr.error(message);
},
complete: function () {
$('.loader').fadeOut();
$('.loader-mask').delay(200).fadeOut('slow');
}
});
});
});
$('#retail_reminder_template_form').on('submit', function (e) {
e.preventDefault();
if (!retailUnlayerEditor || typeof retailUnlayerEditor.exportHtml !== 'function') {
toastr.error('Editor not ready. Please try again.');
return;
}
$('.loader, .loader-mask').fadeIn();
retailUnlayerEditor.exportHtml(function (data) {
var formData = new FormData($('#retail_reminder_template_form')[0]);
formData.set('subject', $('#tpl_subject').val());
formData.set('mailContent', data.html);
formData.set('mailJson', JSON.stringify(data.design));
$.ajax({
url: templateUrl,
type: 'POST',
dataType: 'json',
data: formData,
processData: false,
contentType: false,
success: function (res) {
if (res && res.status) {
toastr.success(res.message || 'Template saved');
hideRetailModal('retail_reminder_template_modal');
} else {
toastr.error((res && res.message) || 'Failed to save template');
}
},
error: function (xhr) {
var message = 'Failed to save template';
if (xhr.responseJSON && xhr.responseJSON.message) {
message = xhr.responseJSON.message;
}
toastr.error(message);
},
complete: function () {
$('.loader').fadeOut();
$('.loader-mask').delay(200).fadeOut('slow');
}
});
});
});
});
})();
</script>