FIX_UI phase 1

This commit is contained in:
sanjeev.p 2026-04-10 12:20:38 +05:30
parent 24fe2b79c4
commit b7e1e01b8a
45 changed files with 4321 additions and 2562 deletions

View File

@ -454,19 +454,44 @@ class LeadsController extends BaseController
'errors' => ['required' => 'Entity Type is required'],
];
$clientNameRules = 'required|regex_match[/^[a-zA-Z0-9_ -]+$/]';
$clientShortNameRules = 'required|regex_match[/^[a-zA-Z0-9_-]+$/]';
// Only validate for uniqueness if it's a new client being specified.
// If an existing client is selected from the dropdown, `client_id` will be present.
if (empty($postData['client_id'])) {
$ignoreClientId = null;
if ($id) { // This is an update of a lead
$lead = $this->leadsModel->find($id);
if ($lead && !empty($lead['is_client_created'])) {
$ignoreClientId = $lead['is_client_created'];
}
}
if ($ignoreClientId) {
$clientNameRules .= '|is_unique[clients.client_name,id,' . $ignoreClientId . ']';
$clientShortNameRules .= '|is_unique[clients.short_name,id,' . $ignoreClientId . ']';
} else {
$clientNameRules .= '|is_unique[clients.client_name]';
$clientShortNameRules .= '|is_unique[clients.short_name]';
}
}
$rules['client_name'] = [
'rules' => 'required|regex_match[/^[a-zA-Z0-9_ -]+$/]',
'rules' => $clientNameRules,
'errors' => [
'required' => 'Client Name is required',
'regex_match' => 'Client Name only letters, numbers, space, hyphens and underscores are allowed',
'is_unique' => 'This Client Name already exists.',
],
];
$rules['client_short_name'] = [
'rules' => 'required|regex_match[/^[a-zA-Z0-9_-]+$/]',
'rules' => $clientShortNameRules,
'errors' => [
'required' => 'Client Short Name is required',
'regex_match' => 'Client Short Name only letters, numbers, hyphens and underscores are allowed'
'required' => 'Client Short Name is required',
'regex_match' => 'Client Short Name only letters, numbers, hyphens and underscores are allowed',
'is_unique' => 'This Client Short Name already exists.',
],
];
}
@ -925,6 +950,11 @@ class LeadsController extends BaseController
$insertCount[] = $insert;
$this->insertLeadStatus($insert, $value['status'], 3);
if ($value['lead_type'] == 1 && $value['status'] == 'won') {
$leadDataForClient = $this->leadsModel->find($insert);
$this->createClientWithLeadData($leadDataForClient);
}
if ($value['lead_form_type'] == 1) {
//for this push the job to the calculateMembersDemography() function
$job_details = new Jobs();
@ -950,6 +980,14 @@ class LeadsController extends BaseController
$this->insertMultiFilesData($data[0]['multi_file_data'], $id, $data[0]['lead_form_type']);
$this->insertLeadStatus($id, $data[0]['status'], 3);
if ($data[0]['lead_type'] == 1 && $data[0]['status'] == 'won') {
$leadDataForClient = $this->leadsModel->find($id);
if (empty($leadDataForClient['is_client_created'])) {
$this->createClientWithLeadData($leadDataForClient);
}
}
return $this->respond(['status' => true, 'lead_id' => $id, 'message' => "Opportunity updated successfully", 'data' => $data], 200);
}
return $this->respond(['status' => false, 'lead_id' => $id, 'message' => "Failed to update Opportunity", 'data' => $data], 200);

View File

@ -78,13 +78,8 @@ table.dataTable tbody td {
float: left !important;
}
.table th,
.table td {
padding: 8px;
}
table.dataTable tbody td {
padding: 4px 4px !important;
.col-12 {
max-width: 98% !important;
}
/* Tooltip Styles */
@ -247,9 +242,12 @@ table.dataTable tbody td {
border:1px solid rgba(41, 139, 142, 1);
}
.dt-buttons.btn-group{
display: grid;
grid-template-columns: 1fr .4fr .5fr .2fr;
.dt-buttons.btn-group{
width: 100%;
display: flex;
align-items: center;
justify-content: flex-end;
gap: 8px;
}
#user-table_filter input{ width:100%; }
@ -361,15 +359,15 @@ table.dataTable tbody td {
<h4 style="position: relative;">Users</h4>
</div>
</div> -->
<table data-custom-table-css="table" class="table table-sm m-0 table-centered dt-responsive nowrap w-100" cellspacing="a" id="user-table">
<table data-custom-table-css="table" class="table mb-0 nowrap" cellspacing="0" id="user-table">
<thead class="bg-light">
<tr>
<th class="font-weight-medium">Name</th>
<th class="font-weight-medium">Email</th>
<th class="font-weight-medium">Mobile No</th>
<th class="font-weight-medium">Role</th>
<th class="font-weight-medium">Status</th>
<th class="font-weight-medium">Action</th>
<th class="font-weight-medium">Name&nbsp;</th>
<th class="font-weight-medium">Email&nbsp;</th>
<th class="font-weight-medium">Mobile No&nbsp;</th>
<th class="font-weight-medium">Role&nbsp;</th>
<th class="font-weight-medium">Status&nbsp;</th>
<th class="font-weight-medium text-center">Action&nbsp;</th>
</tr>
</thead>
<tbody>
@ -384,8 +382,8 @@ table.dataTable tbody td {
<?php if($row->is_active == 1){ echo 'Active'; }else{ echo 'In-Active'; } ?>
</span>
</td>
<td>
<div class="btn-group dropdown" style="position: relative !important;left:0px !important;top:0px !important;">
<td class="text-center table-action-cell">
<div class="btn-group dropdown">
<a href="javascript: void(0);" class="dropdown-toggle arrow-none btn btn-light btn-sm" data-toggle="dropdown" aria-expanded="false"><i class="mdi mdi-dots-horizontal"></i></a>
<div class="dropdown-menu dropdown-menu-right" style="cursor: pointer;">
<a class="dropdown-item btnEdit" data-id="<?php echo $row->id; ?>"><i data-id="<?php echo $row->id; ?>" class="mdi mdi-pencil mr-2 text-muted font-18 vertical-middle btnEdit"></i>Edit</a>
@ -611,7 +609,7 @@ table.dataTable tbody td {
<div class="modal-body p-4">
<div class="">
<form role="form" class="parsley-examples" method="post" id="PartnerForm" action="" enctype="multipart/form-data">
<form role="form" class="parsley-examples" method="post" id="PartnerForm" action="" enctype="multipart/form-data" data-parsley-validate>
<input type="hidden" value="<?= csrf_hash() ?>" name="<?= csrf_token() ?>"/>
<input type="hidden" name="PrimaryKey" id="partner_id" name="id"/>
<div class="form-group">
@ -619,23 +617,22 @@ table.dataTable tbody td {
<div class="form-row">
<div class="form-group col-md-6">
<label for="partner_name">Name<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="partner_name" placeholder="Enter Name" name="name" required>
<input type="text" class="form-control" id="partner_name" placeholder="Enter Name" name="name" required data-parsley-pattern="^[a-zA-Z\s\.]+$" data-parsley-trigger="keyup" data-parsley-pattern-message="Please enter a valid name (letters, spaces, dots only).">
</div>
<div class="form-group col-md-6">
<label for="mobile_no">Mobile Number<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="mobile_no" placeholder="Enter Mobile Number ( starting with 6, 7, 8, or 9 only. )" name="mobile" onkeypress = "return onlyNumbers(event)" maxlength="10" minlength="10" pattern="^[6-9]\d{9}$" data-parsley-type-message="Please enter a valid 10-digit mobile number starting with 6, 7, 8, or 9." data-parsley-required-message="Please enter a valid 10-digit mobile number starting with 6, 7, 8, or 9." required>
<input type="text" class="form-control" id="mobile_no" placeholder="Enter Mobile Number ( starting with 6, 7, 8, or 9 only. )" name="mobile" maxlength="10" minlength="10" pattern="^[6-9]\d{9}$" data-parsley-type-message="Please enter a valid 10-digit mobile number starting with 6, 7, 8, or 9." data-parsley-required-message="Please enter a valid 10-digit mobile number starting with 6, 7, 8, or 9." required data-parsley-trigger="keyup">
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<label for="email_addr">Email<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="email_addr" placeholder="Enter Email" name="email" data-parsley-trigger="change" data-parsley-type="email" required>
<input type="email" class="form-control" id="email_addr" placeholder="Enter Email" name="email" data-parsley-trigger="keyup" data-parsley-type="email" required>
</div>
<div class="form-group col-md-6">
<label for="retention_rate">Retention Rate<span class="text-danger">*</span></label>
<!-- <input type="text" class="form-control" id="retention_rate" placeholder="Enter Retention Rate" name="retention_rate" required> -->
<input type="text" class="form-control" id="retention_rate" name="retention_rate" placeholder="Enter Retention Rate" inputmode="decimal" required>
<input type="text" class="form-control" id="retention_rate" name="retention_rate" placeholder="Enter Retention Rate" inputmode="decimal" required data-parsley-type="number" data-parsley-min="0" data-parsley-min-message="Retention rate cannot be negative.">
</div>
</div>
@ -662,7 +659,7 @@ table.dataTable tbody td {
<div class="form-group text-right m-b-0">
<button class="btn app-btn-outline-secondary mr-2 " type="button" data-dismiss="modal" aria-hidden="true">Cancel</button>
<button class="btn app-btn-secondary waves-effect waves-light" type="button" id="btnPartnerSubmit" onclick="submitPartner(event)">Submit</button>
<button class="btn app-btn-secondary waves-effect waves-light" type="submit" id="btnPartnerSubmit">Submit</button>
</div>
</form>
</div>
@ -1060,11 +1057,15 @@ table.dataTable tbody td {
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666; display:none;"></i>
</div>`,
searchPlaceholder: "Search",
emptyTable: '<div class="text-center text-muted">No Data found</div>'
emptyTable: '<div class="text-center text-muted">No Data found</div>',
paginate: {
previous: '◄',
next: '►'
}
},
initComplete: function () {
$(".dt-buttons").prepend(`
<div class="tab-button mr-2" id="toggleButton" style="float:left;">
<div class="tab-button mr-2" id="toggleButton" style="margin-right:auto;">
<span id="btnUsers" class="highlight-link active">Users</span>
<span class="divider-line"> | </span>
<span id="btnPartners" class="highlight-link">Nhance Partner</span>
@ -1085,6 +1086,26 @@ table.dataTable tbody td {
}
});
function applyBottomRowDropup() {
if (!table) return;
const currentRows = table.rows({ page: 'current' }).nodes().toArray();
$('#user-table tbody tr').removeClass('nh-force-dropup');
$('#user-table tbody tr td.table-action-cell .btn-group.dropdown').removeClass('dropup');
// Mark last two rows on current page to open action menu upward.
const targetCount = Math.min(2, currentRows.length);
for (let i = 0; i < targetCount; i++) {
const row = currentRows[currentRows.length - 1 - i];
if (!row) continue;
$(row).addClass('nh-force-dropup');
$(row).find('td.table-action-cell .btn-group.dropdown').addClass('dropup');
}
}
applyBottomRowDropup();
$('#user-table').on('draw.dt', applyBottomRowDropup);
// IMPORTANT — DataTables draw event REF: TTS
table.on('draw.dt', function () {
@ -1243,6 +1264,8 @@ table.dataTable tbody td {
});
$('body').on('click', '.btnPartnerEdit', function () {
$('#PartnerForm').parsley().reset();
let user = JSON.parse($(this).attr('data-obj')); // ✅ convert back to object
$('#PartnerForm').attr('action', '<?php echo base_url('/user/partner');?>');
$('#partner_id').val(user.id);
@ -1328,13 +1351,6 @@ table.dataTable tbody td {
});
function onlyNumbers(event){
var charcode;
charcode = event.which || event.keyCode;
if(charcode>= 48 && charcode <= 57)return true;
return false;
}
var form = document.getElementById("UserForm");
form.addEventListener("submit", function(event) {
@ -1426,58 +1442,18 @@ form.addEventListener("submit", function(event) {
});
});
function submitPartner(event) {
event.preventDefault();
$('#PartnerForm').on('submit', function(e) {
e.preventDefault();
var $form = $(this);
$form.parsley().validate();
var form = document.getElementById('PartnerForm');
if ($form.parsley().isValid()) {
var btn = document.getElementById('btnPartnerSubmit');
var myModal = new bootstrap.Modal(document.getElementById('nhance-partner-modal'));
// reset any old error styles
form.classList.remove('was-validated');
var name = document.getElementById('partner_name').value.trim();
var mobile = document.getElementById('mobile_no').value.trim();
var email = document.getElementById('email_addr').value.trim();
// var address = document.getElementById('locn').value.trim();
var rate = document.getElementById('retention_rate').value.trim();
var nb = document.getElementById('partner_nhance_branch_id').value.trim();
// Validation checks
if (name === "" || mobile === "" || email === "" || rate === "" || nb === "") {
// console.log(name);
// console.log(mobile);
// console.log(email);
toastr.warning('Please fill all required fields.', 'Warning');
form.classList.add('was-validated');
return;
}
// if (address.length > 1500) {
// toastr.error('Address cannot exceed 1500 characters', 'Warning');
// form.classList.add('was-validated');
// return;
// }
var mobilePattern = /^[6-9]\d{9}$/; // must start with 69 and be 10 digits
if (!mobilePattern.test(mobile)) {
toastr.warning('Please enter a valid 10-digit mobile number starting with 6, 7, 8, or 9.', 'Warning');
document.getElementById('mobile').focus();
return;
}
var emailPattern = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
if (!emailPattern.test(email)) {
toastr.warning('Please enter a valid email address.', 'Warning');
document.getElementById('email').focus();
return;
}
// Disable button during submit
btn.disabled = true;
btn.innerText = "Saving...";
var formData = new FormData(form);
var formData = new FormData(this);
$.ajax({
url: "<?= base_url('user/partner') ?>",
@ -1489,8 +1465,9 @@ form.addEventListener("submit", function(event) {
success: function(response) {
if (response.status === "success") {
toastr.success('Partner saved successfully!', 'Success');
myModal.hide();
form.reset(); // reset form after success
$('#nhance-partner-modal').modal('hide');
$form[0].reset();
$('#partner_nhance_branch_id').val(null).trigger('change.select2');
} else {
toastr.warning(response.message || 'Something went wrong.', 'Warning');
}
@ -1498,41 +1475,38 @@ form.addEventListener("submit", function(event) {
error: function(xhr) {
console.log("Error: " + xhr.statusText);
if (xhr.status === 400) {
let response = JSON.parse(xhr.responseText);
let errorMessages = "";
let seenMessages = []; // Array to store unique messages
if (response.errors) {
$.each(response.errors, function (field, message) {
if (!seenMessages.includes(message)) {
errorMessages += `• ${message}<br>`;
seenMessages.push(message); // Mark this message as "seen"
}
});
toastr.error(errorMessages, 'Validation Error', { "allowHtml": true });
} else {
toastr.warning(response.message || 'Validation failed', 'Warning');
}
}else if (xhr.status === 403) {
let response = JSON.parse(xhr.responseText);
toastr.error(response.message, 'Security Policy');
} else if (xhr.status === 500) {
toastr.error('Something went wrong . Please try again later.', 'Server Error');
let response = JSON.parse(xhr.responseText);
let errorMessages = "";
let seenMessages = [];
if (response.errors) {
$.each(response.errors, function (field, message) {
if (!seenMessages.includes(message)) {
errorMessages += `• ${message}<br>`;
seenMessages.push(message);
}
});
toastr.error(errorMessages, 'Validation Error', { "allowHtml": true });
} else {
toastr.error('An unexpected error occurred. Please try again later.', 'Error');
toastr.warning(response.message || 'Validation failed', 'Warning');
}
console.error('Unexpected error:', xhr.responseText);
} else if (xhr.status === 403) {
let response = JSON.parse(xhr.responseText);
toastr.error(response.message, 'Security Policy');
} else if (xhr.status === 500) {
toastr.error('Something went wrong . Please try again later.', 'Server Error');
} else {
toastr.error('An unexpected error occurred. Please try again later.', 'Error');
}
console.error('Unexpected error:', xhr.responseText);
},
complete: function() {
btn.disabled = false;
btn.innerText = "Submit";
$('#nhance-partner-modal').removeClass('show').hide();
$('.modal-backdrop').remove();
btn.innerText = "Submit";
loadPartner();
}
});
}
}
});
function submitIncentivePartner(event) {
event.preventDefault();
@ -1683,7 +1657,8 @@ form.addEventListener("submit", function(event) {
];
}
table.row.add(rowData);
let newRow = table.row.add(rowData);
$(newRow.node()).find('td:last').addClass('text-center table-action-cell');
});
} else {
// Show placeholder if no data
@ -2122,6 +2097,8 @@ form.addEventListener("submit", function(event) {
// myModal.show(); // open modal
// });
$(document).on('click', '#btnNhancePartnerAdd', function(){
$('#PartnerForm')[0].reset();
$('#PartnerForm').parsley().reset();
$('#partner_name').val('');
$('#email_addr').val('');
$('#partner_id').val('');

View File

@ -1,17 +1,4 @@
<style>
.table th,
.table td {
padding: 8px;
}
table.dataTable tbody td {
padding: 4px 4px !important;
}
table.dataTable thead th {
padding: 4px 4px !important;
}
.col-12{
max-width: 98% !important;
@ -35,15 +22,14 @@ table.dataTable thead th {
</div> -->
<!-- <table data-custom-table-css="table" class="table table-sm table-hover m-0 table-centered dt-responsive nowrap w-100" cellspacing="0"
id="tickets-table"> -->
<div class="table-responsive">
<table data-custom-table-css="table" class="table mb-0 nowrap w-100 table-centered" cellspacing="0" id="tickets-table">
<table data-custom-table-css="table" class="table mb-0 nowrap" cellspacing="0" id="tickets-table">
<thead class="bg-light">
<tr>
<th class="font-weight-medium"><div class="column-header">Advertisement Image Name</div></th>
<th class="font-weight-medium"><div class="column-header">Client Name</div></th>
<th class="font-weight-medium"><div class="column-header">Client Short Name</div></th>
<th class="font-weight-medium"><div class="column-header">Status</div></th>
<th class="font-weight-medium"><div class="column-header">Action</div></th>
<th class="font-weight-medium">Advertisement Image Name&nbsp;</th>
<th class="font-weight-medium">Client Name&nbsp;</th>
<th class="font-weight-medium">Client Short Name&nbsp;</th>
<th class="font-weight-medium">Status&nbsp;</th>
<th class="font-weight-medium text-center">Action&nbsp;</th>
</tr>
</thead>
@ -54,7 +40,7 @@ table.dataTable thead th {
<td class="client_info" ><?php echo $row['client_id'] != 0 ? $row['client_name'] : '-' ; ?></td>
<td class="client_info" ><?php echo $row['client_id'] != 0 ? $row['short_name'] : '-'; ?></td>
<td class="client_info" ><?php echo $row['status']; ?></td>
<td>
<td class="text-center table-action-cell">
<div class="btn-group dropdown">
<a href="javascript: void(0);" class="dropdown-toggle arrow-none btn btn-light btn-sm" data-toggle="dropdown" aria-expanded="false"><i class="mdi mdi-dots-horizontal"></i></a>
<div class="dropdown-menu dropdown-menu-right">
@ -68,7 +54,6 @@ table.dataTable thead th {
<?php } ?>
</tbody>
</table>
</div>
</div>
</div>
</div><!-- end col -->
@ -86,7 +71,7 @@ table.dataTable thead th {
<h4 id="advertise_add_edit">Add Advertise Image </h4>
</div>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<form id="model_form_data" class="px-4" enctype="multipart/form-data">
<form id="model_form_data" class="px-4" enctype="multipart/form-data" data-parsley-validate data-parsley-excluded="[disabled]">
<input type="hidden" name="add_image_id" id="add_image_id" value="0">
<!-- old
@ -126,15 +111,27 @@ table.dataTable thead th {
<div class="form-group">
<label for="branchname">Upload Image</label>
<div class="input-icon">
<input type="file" class="form-control" name="advertise_image" id="advertise_image" accept=".jpg,.jpeg,.png" onchange="PreviewImage();" required>
<input type="file" class="form-control" name="advertise_image" id="advertise_image" accept=".jpg,.jpeg,.png" onchange="PreviewImage();" required
data-parsley-required-message="Image is required"
data-parsley-allowed-ext="true"
data-parsley-max-file-size="true"
data-parsley-exact-dimensions="true"
data-parsley-errors-container="#advertise_image_error">
<i class="mdi mdi-upload additional-icon"></i>
</div>
<div id="advertise_image_error"></div>
</div>
<!-- Bottom section -->
<div class="form-group">
<label for="client_branch">Client <span class="text-danger">*</span></label>
<select class="form-control" id="client_id" name="client_id" required>
<select class="form-control" id="client_id" name="client_id" required
data-parsley-required-message="Client is required"
data-parsley-type="integer"
data-parsley-min="0"
data-parsley-min-message="Invalid client selected"
data-parsley-type-message="Invalid client selected"
data-parsley-errors-container="#client_id_error">
<option value="">Select Client</option>
<option value="0">Default Image</option>
<?php
@ -145,6 +142,7 @@ table.dataTable thead th {
}
?>
</select>
<div id="client_id_error"></div>
</div>
</div>
@ -175,23 +173,18 @@ table.dataTable thead th {
function submitBranch() {
var form = $('#model_form_data');
form.parsley().validate();
if (!form.parsley().isValid()) {
return;
}
const fileInput = $('#advertise_image')[0].files[0]; // get actual file object
const imageId = $('#add_image_id').val();
const clientId = $('#client_id').val();
if (!fileInput) {
toastr.warning('Please upload an image before submitting', 'Warning');
$('#advertise_image').focus();
return;
}
if(!clientId){
toastr.warning('Please Select Client.', 'Warning');
$('#client_id').focus();
return;
}
var url = '<?= base_url("add_advertise_image") ?>';
var formData = new FormData();
formData.append('advertise_image', fileInput);
@ -267,16 +260,26 @@ table.dataTable thead th {
console.log('function called');
var fileInput = document.getElementById("advertise_image");
var file = fileInput.files[0];
var $fileInput = $(fileInput);
$fileInput.removeAttr('data-dimension-valid');
$fileInput.removeAttr('data-size-valid');
$fileInput.removeAttr('data-ext-valid');
if (file) {
var allowedExtensions = ["jpg", "jpeg", "png"];
var fileExtension = file.name.split('.').pop().toLowerCase();
if (!allowedExtensions.includes(fileExtension) || file.size > 200 * 1024) {
toastr.warning('Maximum file size allowed is 200KB.', 'File size exceeds limit.');
fileInput.value = ""; // Clear the file input
document.getElementById("uploadPreview").src = "<?= base_url()."public/assets/images/avatar_2x.png" ?>"; // Remove the preview image
return;
if (!allowedExtensions.includes(fileExtension)) {
$fileInput.attr('data-ext-valid', 'false');
} else {
$fileInput.attr('data-ext-valid', 'true');
}
if (file.size > 200 * 1024) {
$fileInput.attr('data-size-valid', 'false');
} else {
$fileInput.attr('data-size-valid', 'true');
}
var reader = new FileReader();
@ -288,15 +291,18 @@ table.dataTable thead th {
img.onload = function () {
if (img.width !== 1640 || img.height !== 664) {
toastr.warning('Image dimensions must be 1640x664 pixels.', 'Invalid image dimensions.');
fileInput.value = ""; // Clear the file input
$fileInput.attr('data-dimension-valid', 'false');
document.getElementById("uploadPreview").src = "<?= base_url()."public/assets/images/avatar_2x.png" ?>"; // Remove the preview image
} else {
$fileInput.attr('data-dimension-valid', 'true');
document.getElementById("uploadPreview").src = img.src;
}
$fileInput.parsley().validate();
};
};
} else {
document.getElementById("uploadPreview").src = "<?= base_url()."public/assets/images/avatar_2x.png" ?>";
$fileInput.parsley().validate();
}
}
@ -304,7 +310,31 @@ table.dataTable thead th {
var table;
$(document).ready(function() {
window.Parsley.addValidator('allowedExt', {
validateString: function(value, requirement, parsleyInstance) {
return parsleyInstance.$element.attr('data-ext-valid') !== 'false';
},
messages: { en: 'Only JPG, JPEG, PNG allowed' }
});
window.Parsley.addValidator('maxFileSize', {
validateString: function(value, requirement, parsleyInstance) {
return parsleyInstance.$element.attr('data-size-valid') !== 'false';
},
messages: { en: 'Image size must not exceed 200 KB' }
});
window.Parsley.addValidator('exactDimensions', {
validateString: function(value, requirement, parsleyInstance) {
return parsleyInstance.$element.attr('data-dimension-valid') !== 'false';
},
messages: { en: 'Image dimensions must be exactly 1640x664 pixels' }
});
$('#client_id').select2();
$('#client_id').on('select2:select select2:clear', function () {
$(this).parsley().validate();
});
$('#add_image_id').val('');
$('#client_id').val(null).trigger('change');
$('#advertise_image').val('');
@ -336,10 +366,33 @@ table.dataTable thead th {
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666; display:none;"></i>
</div>`,
searchPlaceholder: "Search",
emptyTable: '<div class="text-center text-muted">No Data found</div>'
emptyTable: '<div class="text-center text-muted">No Data found</div>',
paginate: {
previous: '◄',
next: '►'
}
},
paging: true
});
function applyBottomRowDropup() {
if (!table) return;
const currentRows = table.rows({ page: 'current' }).nodes().toArray();
$('#tickets-table tbody tr').removeClass('nh-force-dropup');
$('#tickets-table tbody tr td.table-action-cell .btn-group.dropdown').removeClass('dropup');
const targetCount = Math.min(2, currentRows.length);
for (let i = 0; i < targetCount; i++) {
const row = currentRows[currentRows.length - 1 - i];
if (!row) continue;
$(row).addClass('nh-force-dropup');
$(row).find('td.table-action-cell .btn-group.dropdown').addClass('dropup');
}
}
applyBottomRowDropup();
$('#tickets-table').on('draw.dt', applyBottomRowDropup);
});
// ✅ Define your modal function separately
@ -348,6 +401,10 @@ table.dataTable thead th {
$('#client_id').val(null).trigger('change');
$('#advertise_add_edit').html('Add Advertise Image');
$('#uploadPreview').attr('src', '<?= base_url()."public/assets/images/avatar_2x.png" ?>');
$('#advertise_image').prop('required', true);
if ($('#model_form_data').length) {
$('#model_form_data').parsley().reset();
}
$('#bike_model_login-modal').modal('show'); // ✅ this shows the modal
var myModal = new bootstrap.Modal(document.getElementById('bike_make_login-modal'));
myModal.show();
@ -442,6 +499,10 @@ table.dataTable thead th {
// 5. Clear the file input
$('#advertise_image').val('');
$('#advertise_image').prop('required', false);
if ($('#model_form_data').length) {
$('#model_form_data').parsley().reset();
}
// 6. Show the modal
var myModal = new bootstrap.Modal(document.getElementById('bike_make_login-modal'));

View File

@ -27,7 +27,7 @@
<div class="col-12">
<div class="card">
<div class="card-body">
<table data-custom-table-css="table" class="table table-hover m-0 table-centered dt-responsive w-100" cellspacing="0" id="tickets-table">
<table data-custom-table-css="table" class="table table-hover m-0 table-centered w-100" cellspacing="0" id="tickets-table">
<thead class="bg-light">
<tr>
<th class="font-weight-medium">S.No&nbsp;</th>
@ -65,7 +65,7 @@
<span> <?= $file['status'] ?> </span>
<?php } ?>
</td>
<td>
<td class="text-center table-action-cell">
<div class="btn-group dropdown">
<a href="javascript: void(0);" class="dropdown-toggle arrow-none btn btn-light btn-sm"
data-toggle="dropdown" aria-expanded="false"><i
@ -279,7 +279,7 @@
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
buttons: [
{
text: 'Add',
text: '<i class="mdi mdi-plus" ></i><span class=" btn-custom"> Add </span>',
className: 'buttons-html5 addbtnStyle',
action: function (e, dt, node, config) {
openDumpUploadModal();
@ -296,11 +296,33 @@
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666; display:none;"></i>
</div>`,
searchPlaceholder: "Search",
emptyTable: '<div class="text-center text-muted">No Data found</div>'
emptyTable: '<div class="text-center text-muted">No Data found</div>',
paginate: {
previous: '◄',
next: '►'
}
},
paging: true, // Enable pagination
pageLength: 15, // Set default number of rows per page (optional)
pageLength: 10, // Set default number of rows per page (optional)
});
function applyBottomRowDropup() {
if (!ticketsTable) return;
const currentRows = ticketsTable.DataTable().rows({ page: 'current' }).nodes().toArray();
$('#tickets-table tbody tr').removeClass('nh-force-dropup');
$('#tickets-table tbody tr td.table-action-cell .btn-group.dropdown').removeClass('dropup');
const targetCount = Math.min(2, currentRows.length);
for (let i = 0; i < targetCount; i++) {
const row = currentRows[currentRows.length - 1 - i];
if (!row) continue;
$(row).addClass('nh-force-dropup');
$(row).find('td.table-action-cell .btn-group.dropdown').addClass('dropup');
}
}
applyBottomRowDropup();
ticketsTable.on('draw.dt', applyBottomRowDropup);
} else {
console.error("Table not found.");
}

View File

@ -10,7 +10,7 @@
<div class="modal-body">
<div class="">
<form role="form" class="parsley-examples" id="CDMasterForm" enctype="multipart/form-data">
<form role="form" class="parsley-examples" id="CDMasterForm" enctype="multipart/form-data" data-parsley-validate>
<input type="hidden" name="PrimaryKey" id="CD_Master_ID" />
<div class="form-group">
@ -67,12 +67,22 @@
<div class="form-row">
<div class="form-group col-md-6">
<label for="mobile">CD Account Number<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="cd_ac_no_for_cd_master" name="cd_ac_no" pattern="[a-zA-Z0-9\/_-]+" title="Only letters, numbers, /, -, and _ are allowed" required> <small style="margin-left: 10px;font-size: 10px;"><span class="text-danger" id="cd_ac_no_for_cd_master_errorr"></span></small>
<input type="text" class="form-control" id="cd_ac_no_for_cd_master" name="cd_ac_no"
required
data-parsley-pattern="^[a-zA-Z0-9\/_-]+$"
data-parsley-pattern-message="Only letters, numbers, /, -, and _ are allowed."
data-parsley-trigger="keyup">
<small style="margin-left: 10px;font-size: 10px;"><span class="text-danger" id="cd_ac_no_for_cd_master_errorr"></span></small>
</div>
<div class="form-group col-md-6">
<label for="mobile">Opening Amount<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="opening_bal" name="opening_bal" onkeypress="return onlyNumbers(event)" required data-parsley-type="digits">
<input type="text" class="form-control" id="opening_bal" name="opening_bal"
required
data-parsley-type="number"
data-parsley-min="0"
data-parsley-type-message="Opening amount must be numeric."
data-parsley-min-message="Opening amount cannot be negative.">
</div>
</div>
@ -80,7 +90,7 @@
<div id="cdButtonWrapper" class="form-group text-right m-b-0">
<button class="btn app-btn-outline-secondary mr-2 " type="button" class="close" data-dismiss="modal" aria-hidden="true" onclick="resetCdMasterFormModal()">Cancel</button>
<button class="btn btn-primary waves-effect waves-light" id="cd_master_btn_Submit">Submit</button>
<button type="submit" class="btn btn-primary waves-effect waves-light" id="cd_master_btn_Submit">Submit</button>
<!-- <button type="button" class="btn btn-secondry waves-effect waves-light mr-1" data-dismiss="modal" aria-hidden="true">Close</button> -->
</div>
</form>
@ -235,13 +245,6 @@
});
});
function onlyNumbers(event) {
var charcode;
charcode = event.which || event.keyCode;
if (charcode >= 48 && charcode <= 57 || charcode == 46) return true;
return false;
}
function resetCdMasterFormModal(){
console.log("resetCdMasterFormModal() function called");
@ -365,33 +368,4 @@
}
}
$(document).ready(function() {
// Target the input by its ID
$('#cd_ac_no_for_cd_master').on('keypress', function(e) {
// Get the character code
var keyCode = e.which || e.keyCode;
var char = String.fromCharCode(keyCode);
// Regular Expression: allow a-z, A-Z, 0-9, /, -, _
// If the character doesn't match, prevent it from being typed
var regex = /^[a-zA-Z0-9\/_-]+$/;
if (!regex.test(char)) {
e.preventDefault();
// Optional: Show a quick error message in your span
$('#cd_ac_no_for_cd_master_errorr').text('Character not allowed').fadeOut(2000, function() {
$(this).text('').show();
});
return false;
}
});
// Also handle "Paste" to ensure invalid data isn't pasted in
$('#cd_ac_no_for_cd_master').on('input', function() {
var value = $(this).val();
var sanitized = value.replace(/[^a-zA-Z0-9\/_-]/g, '');
$(this).val(sanitized);
});
});
</script>

View File

@ -1,17 +1,4 @@
<style>
.table th,
.table td {
padding: 8px;
}
table.dataTable tbody td {
padding: 4px 4px !important;
}
table.dataTable thead th {
padding: 4px 4px !important;
}
.col-12 {
max-width: 98% !important;
@ -19,38 +6,8 @@ table.dataTable thead th {
.column-header {margin-right: 10px;}
.custom-dropdown-menu {
display: none;
position: absolute;
background-color: #ffffff !important;
border: 1px solid rgba(0, 0, 0, 0.15);
border-radius: 0.25rem;
padding: 0.5rem 0;
min-width: 10rem;
z-index: 9999;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.custom-dropdown-menu .dropdown-item {
display: block !important;
width: 100% !important;
padding: 0.5rem 1rem !important;
color: #212529 !important;
text-decoration: none !important;
background-color: transparent !important;
}
.custom-dropdown-menu .dropdown-item:hover {
background-color: #f8f9fa !important;
color: #16181b !important;
cursor: pointer !important;
}
.dataTables_filter {
position: absolute;
}
.dataTables_length label {height: 21px !important;}
@ -71,21 +28,21 @@ table.dataTable thead th {
</div>
</div> -->
<table data-custom-table-css="table" id="scroll-horizontal-datatable" class="table w-100 nowrap text-custom-black text-custom app-datatable">
<table data-custom-table-css="table" id="scroll-horizontal-datatable" class="table mb-0 nowrap" cellspacing="0">
<thead class="bg-light">
<tr>
<th class="font-weight-medium"> <div class="column-header">S.No&nbsp; </div> </th>
<th class="font-weight-medium"> <div class="column-header">Client Name&nbsp; </div> </th>
<th class="font-weight-medium"> <div class="column-header">Insurer Name&nbsp; </div> </th>
<th class="font-weight-medium"> <div class="column-header">Insurer Branch Name&nbsp; </div> </th>
<th class="font-weight-medium"> <div class="column-header">Opening Date&nbsp; </div> </th>
<th class="font-weight-medium"> <div class="column-header">CD Account No&nbsp; </div> </th>
<th class="font-weight-medium"> <div class="column-header">Opening Amount&nbsp; </div> </th>
<th class="font-weight-medium"> <div class="column-header">Date/User&nbsp; </div> </th>
<th class="font-weight-medium"> <div class="column-header">Action&nbsp; </div> </th>
<th class="font-weight-medium">S.No&nbsp;</th>
<th class="font-weight-medium">Client Name&nbsp;</th>
<th class="font-weight-medium">Insurer Name&nbsp;</th>
<th class="font-weight-medium">Insurer Branch Name&nbsp;</th>
<th class="font-weight-medium">Opening Date&nbsp;</th>
<th class="font-weight-medium">CD Account No&nbsp;</th>
<th class="font-weight-medium">Opening Amount&nbsp;</th>
<th class="font-weight-medium">Date/User&nbsp;</th>
<th class="font-weight-medium text-center">Action&nbsp;</th>
</tr>
</thead>
<tbody class="app-table-body">
<tbody>
<?php foreach($CD_Master_Data as $index => $row){ ?>
<tr>
<td class="text-center"><?= $index + 1; ?></td>
@ -96,7 +53,7 @@ table.dataTable thead th {
<td><?php echo $row['cd_ac_no']; ?></td>
<td><?php echo $row['opening_bal']; ?></td>
<td><?php echo date('d-M-Y h:i A', strtotime($row['created_at'])) ?> <br>by <?php echo $row['user_name']; ?></td>
<td>
<td class="text-center table-action-cell">
<div class="btn-group dropdown">
<a href="javascript: void(0);"class="dropdown-toggle arrow-none btn btn-light btn-sm" data-toggle="dropdown" aria-expanded="false"><i class="mdi mdi-dots-horizontal"></i></a>
<div class="dropdown-menu dropdown-menu-right">
@ -107,8 +64,8 @@ table.dataTable thead th {
</div>
</div>
</td>
<?php } ?>
</tr>
<?php } ?>
</tbody>
</table>
</div>
@ -119,105 +76,11 @@ table.dataTable thead th {
<?php include("cd_master_add_modal.php"); ?>
<script>
document.addEventListener("DOMContentLoaded", function () {
const table = document.getElementById("scroll-horizontal-datatable");
// Create custom dropdown
function createCustomDropdown(row) {
const originalDropdown = row.querySelector('.dropdown-menu');
if (!originalDropdown) return null;
const customDropdown = document.createElement('div');
customDropdown.className = 'custom-dropdown-menu';
customDropdown.innerHTML = originalDropdown.innerHTML;
return customDropdown;
}
let activeDropdown = null;
// Add click event listener to rows
table.querySelectorAll("tbody tr").forEach(row => {
const customDropdown = createCustomDropdown(row);
if (!customDropdown) return;
row.customDropdown = customDropdown;
document.body.appendChild(customDropdown);
row.addEventListener("click", function(event) {
// Ignore clicks on the last column (action column)
if (event.target.closest('td') === row.lastElementChild) {
return;
}
// Hide any active dropdown
if (activeDropdown && activeDropdown !== customDropdown) {
activeDropdown.style.display = 'none';
}
// Toggle the dropdown display
const isActive = customDropdown.style.display === 'block';
customDropdown.style.display = isActive ? 'none' : 'block';
// Set the position of the dropdown
const rect = event.target.closest('td').getBoundingClientRect();
customDropdown.style.position = 'absolute';
customDropdown.style.left = `${rect.left}px`;
customDropdown.style.top = `${rect.bottom + 5}px`;
// Set as active dropdown
activeDropdown = isActive ? null : customDropdown;
event.stopPropagation();
});
// Preserve click handlers for modal actions
customDropdown.querySelectorAll('.dropdown-item').forEach(item => {
item.addEventListener('click', function(e) {
const isModalAction = this.hasAttribute('data-toggle') && this.getAttribute('data-toggle') === 'modal';
if (isModalAction) {
return; // Allow modal to function normally
}
// Close the dropdown if it's not related to modal
if (activeDropdown) {
activeDropdown.style.display = 'none';
activeDropdown = null;
}
const onclickAttr = this.getAttribute('onclick');
if (onclickAttr) {
eval(onclickAttr);
}
const href = this.getAttribute('href');
if (href && href !== '#') {
window.location.href = href;
}
e.stopPropagation();
});
});
});
// Close dropdown when clicking outside
document.addEventListener("click", function() {
if (activeDropdown) {
activeDropdown.style.display = 'none';
activeDropdown = null;
}
});
});
</script>
<script>
$(document).ready(function() {
$('#scroll-horizontal-datatable').DataTable({
var table = $('#scroll-horizontal-datatable').DataTable({
scrollX: true,
// dom: "<'row'<'col-sm-6'f><'col-sm-6 text-right'B>>" +
// "<'row'<'col-sm-12'tr>>" +
@ -270,7 +133,11 @@ table.dataTable thead th {
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666; display:none;"></i>
</div>`,
searchPlaceholder: "Search",
emptyTable: '<div class="text-center text-muted">No Data found</div>'
emptyTable: '<div class="text-center text-muted">No Data found</div>',
paginate: {
previous: '◄',
next: '►'
}
},
paging: true,
pageLength: 10,
@ -303,6 +170,23 @@ table.dataTable thead th {
// },
// paging: true,
});
function applyBottomRowDropup() {
if (!table) return;
const currentRows = table.rows({ page: 'current' }).nodes().toArray();
$('#scroll-horizontal-datatable tbody tr').removeClass('nh-force-dropup');
$('#scroll-horizontal-datatable tbody tr td.table-action-cell .btn-group.dropdown').removeClass('dropup');
const targetCount = Math.min(2, currentRows.length);
for (let i = 0; i < targetCount; i++) {
const row = currentRows[currentRows.length - 1 - i];
if (!row) continue;
$(row).addClass('nh-force-dropup');
$(row).find('td.table-action-cell .btn-group.dropdown').addClass('dropup');
}
}
applyBottomRowDropup();
$('#scroll-horizontal-datatable').on('draw.dt', applyBottomRowDropup);
})
$(document).ready(function(){

View File

@ -131,7 +131,7 @@
</div>
</div>
<table data-custom-table-css="table" class="table table-hover m-0 table-centered dt-responsive w-100" cellspacing="0" id="tickets-table">
<table data-custom-table-css="table" class="table table-hover m-0 table-centered w-100" cellspacing="0" id="tickets-table">
<thead class="bg-light">
<tr>
<th class="font-weight-medium">S.No&nbsp;</th>
@ -174,7 +174,7 @@
<span style="color : #34A853 ;"> <?= $file['status'] ?> </span>
<?php } ?>
</td>
<td>
<td class="text-center table-action-cell">
<div class="btn-group dropdown">
<a href="javascript: void(0);" class="dropdown-toggle arrow-none btn btn-light btn-sm"
data-toggle="dropdown" aria-expanded="false"><i
@ -486,7 +486,7 @@
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
buttons: [
{
text: 'Add',
text: '<i class="mdi mdi-plus" ></i><span class=" btn-custom"> Add </span>',
className: 'buttons-html5 addbtnStyle',
action: function (e, dt, node, config) {
openDumpUploadModal();
@ -503,11 +503,33 @@
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666; display:none;"></i>
</div>`,
searchPlaceholder: "Search",
emptyTable: '<div class="text-center text-muted">No Data found</div>'
emptyTable: '<div class="text-center text-muted">No Data found</div>',
paginate: {
previous: '◄',
next: '►'
}
},
paging: true, // Enable pagination
pageLength: 15, // Set default number of rows per page (optional)
pageLength: 10, // Set default number of rows per page (optional)
});
function applyBottomRowDropup() {
if (!ticketsTable) return;
const currentRows = ticketsTable.DataTable().rows({ page: 'current' }).nodes().toArray();
$('#tickets-table tbody tr').removeClass('nh-force-dropup');
$('#tickets-table tbody tr td.table-action-cell .btn-group.dropdown').removeClass('dropup');
const targetCount = Math.min(2, currentRows.length);
for (let i = 0; i < targetCount; i++) {
const row = currentRows[currentRows.length - 1 - i];
if (!row) continue;
$(row).addClass('nh-force-dropup');
$(row).find('td.table-action-cell .btn-group.dropdown').addClass('dropup');
}
}
applyBottomRowDropup();
ticketsTable.on('draw.dt', applyBottomRowDropup);
} else {
console.error("Table not found.");
}

View File

@ -308,7 +308,7 @@
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
buttons: [
{
text: 'Add',
text: '<i class="mdi mdi-plus"></i><span class="btn-custom"> Add </span>',
className: 'buttons-html5 addbtnStyle',
action: function (e, dt, node, config) {
openDumpUploadModal();

View File

@ -126,6 +126,9 @@ input:checked + .slider-branch-contact:before {
</tr>
</thead>
<tbody id="branch_list">
<tr id="branch_no_data_row">
<td colspan="3" class="text-center">No data available</td>
</tr>
</tbody>
</table>
</div>
@ -306,6 +309,16 @@ var branch_form_action = '';
var contactCount = 1;
var branch_PrimaryKey = $('#client_id_for_client_branch').val();
function ensureBranchNoDataRow() {
const branchList = $('#branch_list');
const hasDataRows = branchList.find('tr').not('#branch_no_data_row').length > 0;
if (hasDataRows) {
$('#branch_no_data_row').remove();
} else if ($('#branch_no_data_row').length === 0) {
branchList.append('<tr id="branch_no_data_row"><td colspan="3" class="text-center">No data available</td></tr>');
}
}
$(document).ready(function() {
branch_PrimaryKey = $('#client_id_branch').val();
@ -350,8 +363,11 @@ $(document).ready(function() {
});
$('#branch_list').append(branchTable);
ensureBranchNoDataRow();
}
ensureBranchNoDataRow();
});
$('#btnBranchAdd').click(function() {
@ -550,6 +566,7 @@ $("#branch_form").submit(function(event) {
});
$('#branch_list').append(branchTableInsert);
ensureBranchNoDataRow();
},
error: function(xhr, status, error) {
console.error(xhr.responseText);

View File

@ -46,6 +46,11 @@
height: 0;
}
.switch-label .slider,
.switch-label .slider_blue {
pointer-events: none;
}
.slider,
.slider_blue {
position: relative;
@ -89,14 +94,45 @@ input:checked + .slider_blue::before {
user-select: none;
}
#table-client-policy_wrapper .row>.col-sm-12.col-md-6:first-child{
display: none;
}
#table-client-policy_filter{
text-align: left;
}
#table-client-policy{
width: 100% !important;
}
#table-client-policy_wrapper .datatable-search-wrapper{
width: auto !important;
}
#table-client-policy thead th{
white-space: nowrap;
vertical-align: middle;
}
#table-client-policy_filter input{
padding-right: 34px !important;
width: 220px !important;
}
#table-client-policy tbody td.dataTables_empty{
text-align: center !important;
padding: 18px 12px !important;
color: #6c757d !important;
font-weight: 500;
}
#table-client-policy_wrapper .row.align-items-center{
flex-wrap: nowrap;
}
#table-client-policy_wrapper .dataTables_info,
#table-client-policy_wrapper .dataTables_length,
#table-client-policy_wrapper .dataTables_paginate{
white-space: nowrap;
}
</style>
<div class="save-indicator" id="saveIndicator">Saved</div>
@ -119,7 +155,7 @@ input:checked + .slider_blue::before {
</div>
<div class="table-responsive" id="table_list">
<table data-custom-table-css="table" class="table table-borderless table mb-0" id="table-client-policy">
<table data-custom-table-css="table" class="table table-borderless table mb-0 w-100 nowrap" id="table-client-policy">
<thead class="">
<tr style="color:black !important;">
<th>Insurer</th>
@ -569,23 +605,32 @@ input:checked + .slider_blue::before {
paging: true,
searching: true,
autoWidth: false,
responsive: true,
scrollX: true,
responsive: false,
dom: "<'row'<'col-12 d-flex justify-content-between align-items-center'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'>>>" +
"<'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>>",
language: {
search: `
<div class="datatable-search-wrapper" style="position:relative; display:inline-block; width:100%;">
<div class="datatable-search-wrapper" style="position:relative; display:inline-block;">
_INPUT_
<i class="mdi mdi-magnify datatable-search-icon"
style="position:absolute; right:122px !important; top:50%; transform:translateY(-50%); color:#666;"></i>
style="position:absolute; right:11px; top:50%; transform:translateY(-53%); color:#666;"></i>
<i class="mdi mdi-close-circle datatable-clear-icon"
style="position:absolute; right:122px !important; top:50%; transform:translateY(-50%); color:#666; display:none; cursor:pointer;"></i>
style="position:absolute; right:11px; top:50%; transform:translateY(-53%); color:#666; display:none;"></i>
</div>
`,
searchPlaceholder: "Search",
emptyTable: '<div class="text-center text-muted">No Data found</div>'
emptyTable: '<div class="text-center text-muted">No data available</div>',
zeroRecords: '<div class="text-center text-muted">No data available</div>'
},
});
setTimeout(function () {
const dt = $('#table-client-policy').DataTable();
dt.columns.adjust().draw(false);
}, 0);
@ -2664,7 +2709,7 @@ input:checked + .slider_blue::before {
<script>
$(document).ready(function () {
$('#chk-show-expired').on('change', function () {
$(document).on('change', '#chk-show-expired', function () {
const showExpired = $(this).is(':checked');
@ -2795,35 +2840,59 @@ $(document).ready(function () {
});
const table = $('#table-client-policy').DataTable();
table.clear().destroy();
if ($.fn.DataTable.isDataTable('#table-client-policy')) {
$('#table-client-policy').DataTable().clear().destroy();
}
$('#table-client-policy tbody').html(policyTable); // replace tbody content
$('#table-client-policy').DataTable({
const policyTableInstance = $('#table-client-policy').DataTable({
paging: true,
searching: true,
autoWidth: false,
responsive: true,
// language: {
// search: `
// <div class="datatable-search-wrapper" style="position:relative; display:inline-block; width:100%;">
// _INPUT_
// <i class="mdi mdi-magnify datatable-search-icon"
// style="position:absolute; right:122px !important; top:50%; transform:translateY(-50%); color:#666;"></i>
scrollX: true,
responsive: false,
dom: "<'row'<'col-12 d-flex justify-content-between align-items-center'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'>>>" +
"<'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>>",
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:11px; top:50%; transform:translateY(-53%); color:#666;"></i>
// <i class="mdi mdi-close-circle datatable-clear-icon"
// style="position:absolute; right:122px !important; top:50%; transform:translateY(-50%); color:#666; display:none; cursor:pointer;"></i>
// </div>
// `,
// searchPlaceholder: "Search",
// emptyTable: '<div class="text-center text-muted">No Data found</div>'
// },
<i class="mdi mdi-close-circle datatable-clear-icon"
style="position:absolute; right:11px; top:50%; transform:translateY(-53%); color:#666; display:none;"></i>
</div>
`,
searchPlaceholder: "Search",
emptyTable: '<div class="text-center text-muted">No data available</div>',
zeroRecords: '<div class="text-center text-muted">No data available</div>'
},
});
setTimeout(function () {
policyTableInstance.columns.adjust().draw(false);
}, 0);
});
// Fix header/body alignment when tab becomes visible
$(document).on('shown.bs.tab', 'a[data-toggle="tab"], a[data-bs-toggle="tab"]', function () {
const target = $(this).attr('href') || $(this).data('target');
if (target === '#police-tab' && $.fn.DataTable.isDataTable('#table-client-policy')) {
const dt = $('#table-client-policy').DataTable();
dt.columns.adjust().draw(false);
}
});
$(window).on('resize', function () {
if ($.fn.DataTable.isDataTable('#table-client-policy')) {
$('#table-client-policy').DataTable().columns.adjust();
}
});
});
</script>

View File

@ -1,8 +1,7 @@
<style>
.dataTables_filter {
position: absolute;
.col-12 {
max-width: 98% !important;
}
.dataTables_length label {height: 21px !important;}
.custom-dropdown-menu {
display: none;
@ -30,6 +29,9 @@
color: #16181b !important;
cursor: pointer !important;
}
.dataTables_length label {height: 21px !important;}
</style>
<!-- End ADD and EDIT Page HTML -->
@ -37,15 +39,15 @@
<div class="col-12">
<div class="card">
<div class="card-body">
<table data-custom-table-css="table" class="table table-sm m-0 table-centered dt-responsive nowrap w-100" cellspacing="a" id="user-table">
<table data-custom-table-css="table" class="table mb-0 nowrap" cellspacing="0" id="user-table">
<thead class="bg-light">
<tr>
<th class="font-weight-medium">S.No.</th>
<th class="font-weight-medium">Category</th>
<th class="font-weight-medium">Question</th>
<th class="font-weight-medium">S.No.&nbsp;</th>
<th class="font-weight-medium">Category&nbsp;</th>
<th class="font-weight-medium">Question&nbsp;</th>
<!-- <th class="font-weight-medium">Answer</th> -->
<th class="font-weight-medium">Status</th>
<th class="font-weight-medium">Action</th>
<th class="font-weight-medium">Status&nbsp;</th>
<th class="font-weight-medium text-center">Action&nbsp;</th>
</tr>
</thead>
<tbody>
@ -76,8 +78,8 @@
<?php if($row['is_active'] == 1){ echo 'Active'; }else{ echo 'In-Active'; } ?>
</span>
</td>
<td>
<div class="btn-group dropdown" style="position: relative !important;left:0px !important;top:0px !important;">
<td class="text-center table-action-cell">
<div class="btn-group dropdown">
<a href="javascript: void(0);" class="dropdown-toggle arrow-none btn btn-light btn-sm" data-toggle="dropdown" aria-expanded="false"><i class="mdi mdi-dots-horizontal"></i></a>
<div class="dropdown-menu dropdown-menu-right" style="cursor: pointer;">
<a class="dropdown-item" data-id="<?= $row['id']; ?>" onclick="handleSaveEditAndDelete('edit', this)">
@ -116,7 +118,12 @@
<div class="form-row">
<div class="form-group col-md-12">
<label for="category">Category<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="category" name="category" placeholder="Enter Category" required>
<input type="text" class="form-control" id="category" name="category" placeholder="Enter Category" required
data-parsley-pattern="^[a-zA-Z0-9 \/\-\.\,&quot;\']+$"
data-parsley-pattern-message="Category can contain only letters, numbers, spaces, and these characters: / - . , &quot; '"
data-parsley-maxlength="100"
data-parsley-maxlength-message="Category cannot exceed 100 characters"
data-parsley-trigger="keyup input">
<small class="form-text text-muted text-end text-right">
Please enter the sub-category separated by '/'. For Example: Category / Category-1 / Category-2
</small>
@ -126,18 +133,26 @@
<div class="form-row">
<div class="form-group col-md-12">
<label for="question">Question<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="question" name="question" placeholder="Enter Question" required>
<input type="text" class="form-control" id="question" name="question" placeholder="Enter Question" required
data-parsley-pattern="^[a-zA-Z0-9 _\-\.,;:!?()&\/]+$"
data-parsley-pattern-message="Question can contain only letters, numbers, spaces, and these characters: . , ; : ! ? ( ) & / -"
data-parsley-maxlength="1000"
data-parsley-maxlength-message="Question cannot exceed 1000 characters"
data-parsley-trigger="keyup input">
</div>
</div>
<div class="form-row">
<div class="form-group col-md-12">
<label for="answer">Answer<span class="text-danger">*</span></label>
<!-- <textarea id="answer" name="answer" class="form-control" required></textarea> -->
<textarea id="answer" name="answer" class="form-control answer" rows="7">
<textarea id="answer" name="answer" class="form-control answer" rows="7" required
data-parsley-maxlength="5000"
data-parsley-maxlength-message="Answer cannot exceed 5000 characters">
<!-- <h5>Hello {USER_NAME}, </h5>
<p>We create simple, flat & responsive custom mail template.</p>
<p>Please, write text here!</p> -->
</textarea>
<div id="answer-errors"></div>
</div>
</div>
</div>
@ -155,10 +170,6 @@
// let faqEditor;
let editor;
// ClassicEditor
// .create(document.querySelector('#answer'))
// .then(editor => {
@ -207,9 +218,13 @@
// 2. Initialize
editor = Jodit.make("#answer", editorConfig);
editor.events.on('change keyup', function () {
$('#answer').val(editor.value);
$('#answer').parsley().validate();
});
var ticketsTable = $('#user-table');
ticketsTable.DataTable({
var table = ticketsTable.DataTable({
scrollX: true,
// dom: "<'row'<'col-sm-7'f><'col-sm-5 text-right'B>>" + // Filter left, buttons right
// "<'row'<'col-sm-12'tr>>" +
@ -220,8 +235,7 @@
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
buttons: [
{
text: 'Add',
className: 'btn app-btn-primary mr-2',
text: '<i class="mdi mdi-plus"></i><span class="btn-custom"> Add </span>',
action: function(e, dt, node, config) {
resetValues();openModal();
}
@ -253,12 +267,59 @@
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666; display:none;"></i>
</div>`,
searchPlaceholder: "Search",
emptyTable: '<div class="text-center text-muted">No Data found</div>'
emptyTable: '<div class="text-center text-muted">No Data found</div>',
paginate: {
previous: '◄',
next: '►'
}
},
paging: true, // Enable pagination
pageLength: 10, // Set default number of rows per page (optional)
// ordering: false,
});
function placeActionDropdown($toggle) {
const $group = $toggle.closest('.btn-group.dropdown');
const $menu = $group.find('.dropdown-menu');
if (!$group.length || !$menu.length) return;
// Reset previous state before recalculating placement.
$group.removeClass('dropup');
const toggleRect = $toggle[0].getBoundingClientRect();
const menuHeight = $menu.outerHeight() || 120;
const spaceBelow = window.innerHeight - toggleRect.bottom;
const spaceAbove = toggleRect.top;
// Open upward only when bottom space is insufficient.
if (spaceBelow < menuHeight + 12 && spaceAbove > menuHeight) {
$group.addClass('dropup');
}
}
$('#user-table').on('show.bs.dropdown', '.table-action-cell .dropdown-toggle', function () {
const $toggle = $(this);
const $scrollBody = $toggle.closest('.dataTables_scrollBody');
if ($scrollBody.length) {
$scrollBody.data('prev-overflow-y', $scrollBody.css('overflow-y'));
$scrollBody.css('overflow-y', 'visible');
}
placeActionDropdown($toggle);
});
$('#user-table').on('hide.bs.dropdown', '.table-action-cell .dropdown-toggle', function () {
const $scrollBody = $(this).closest('.dataTables_scrollBody');
const prevOverflowY = $scrollBody.data('prev-overflow-y');
if ($scrollBody.length) {
$scrollBody.css('overflow-y', prevOverflowY || 'auto');
}
});
ticketsTable.on('draw.dt', function () {
$('#user-table tbody tr td.table-action-cell .btn-group.dropdown').removeClass('dropup');
});
});
$('.close').click(function(){ resetValues(); })
@ -404,115 +465,23 @@
return;
}
if (typeof editor !== 'undefined') {
// Strip HTML tags and trim whitespace to see if there is actual text
const plainText = editor.value.replace(/<[^>]*>/g, '').trim();
// Check if it's truly empty or just contains empty tags like <p><br></p>
if (editor.value === '' || plainText === '') {
toastr.warning("Answer is required", 'Warning');
return; // Stop the function here
}
var isValid = $(this).parsley({
excluded: 'input[type=button], input[type=submit], input[type=reset], input[type=hidden]'
}).validate();
// Block unsafe HTML/JS patterns
if (hasUnsafeHtml(editor.value)) {
toastr.error("Answer contains restricted tags or attributes. Script tags, iframes and event handlers (like onclick, onerror, onmouseover) are not allowed.", "Validation Error");
return;
}
}
if (!isValid) return;
// If valid, submit via AJAX
handleSaveEditAndDelete('submit');
});
</script>
<script>
document.addEventListener("DOMContentLoaded", function() {
const table = document.getElementById("user-table");
// Create custom dropdown
function createCustomDropdown(row) {
// Get the original dropdown items
const originalDropdown = row.querySelector('.dropdown-menu');
if (!originalDropdown) return null;
// Create new dropdown with proper background and spacing
const customDropdown = document.createElement('div');
customDropdown.className = 'custom-dropdown-menu';
// Copy inner content while maintaining icon alignment
customDropdown.innerHTML = originalDropdown.innerHTML;
return customDropdown;
}
let activeDropdown = null;
// Add click event listener to rows
table.querySelectorAll("tbody tr").forEach(row => {
const customDropdown = createCustomDropdown(row);
if (!customDropdown) return;
// Append the custom dropdown to the body
document.body.appendChild(customDropdown);
row.addEventListener("click", function(event) {
// Ignore clicks on the action column
if (event.target.closest('td:last-child')) {
return;
}
// Hide any active dropdown
if (activeDropdown) {
activeDropdown.style.display = 'none';
}
// Get click position
const rect = event.target.getBoundingClientRect();
// Position the dropdown with some offset
customDropdown.style.display = 'block';
customDropdown.style.position = 'fixed';
customDropdown.style.left = `${rect.left}px`;
customDropdown.style.top = `${rect.bottom + 5}px`; // Add 5px gap
// Set as active dropdown
activeDropdown = customDropdown;
event.stopPropagation();
});
// Preserve click handlers and add auto-close
customDropdown.querySelectorAll('.dropdown-item').forEach(item => {
item.addEventListener('click', function(e) {
const onclickAttr = this.getAttribute('onclick');
if (onclickAttr) {
eval(onclickAttr);
}
const href = this.getAttribute('href');
if (href && href !== '#') {
window.location.href = href;
}
// Close the dropdown after handling the click
if (activeDropdown) {
activeDropdown.style.display = 'none';
activeDropdown = null;
}
e.stopPropagation();
});
});
});
// Close dropdown when clicking outside
document.addEventListener("click", function() {
if (activeDropdown) {
activeDropdown.style.display = 'none';
activeDropdown = null;
}
});
// Restrict user typing to allow Regex only characters
$('#category').on('input', function() {
this.value = this.value.replace(/[^a-zA-Z0-9 \/\-.,"']/g, '');
});
</script>
$('#question').on('input', function() {
this.value = this.value.replace(/[^a-zA-Z0-9 _\-.,;:!?()&\/]/g, '');
});
</script>

View File

@ -39,6 +39,39 @@
font-size: 13px;
}
#tickets-table{
width: 100% !important;
}
#tickets-table thead th{
white-space: nowrap;
vertical-align: middle;
}
#tickets-table_wrapper .datatable-search-wrapper{
width: auto !important;
}
#tickets-table_wrapper .dataTables_filter input{
padding-right: 34px !important;
width: 220px !important;
}
#tickets-table_wrapper .row.align-items-center{
flex-wrap: nowrap;
}
#tickets-table_wrapper .dataTables_info,
#tickets-table_wrapper .dataTables_length,
#tickets-table_wrapper .dataTables_paginate{
white-space: nowrap;
}
#tickets-table_wrapper .datatable-search-icon,
#tickets-table_wrapper .datatable-clear-icon{
right: 11px !important;
}
</style>
<div class="col-12">
@ -542,6 +575,17 @@
paging: true,
});
setTimeout(function () {
const dt = $('#tickets-table').DataTable();
dt.columns.adjust().draw(false);
}, 0);
});
$(window).on('resize', function () {
if ($.fn.DataTable.isDataTable('#tickets-table')) {
$('#tickets-table').DataTable().columns.adjust();
}
});
</script>

View File

@ -1,34 +1,8 @@
<style>
.dataTables_filter {
position: absolute;
.col-12 {
max-width: 98% !important;
}
.dataTables_length label {height: 21px !important;}
.custom-dropdown-menu {
display: none;
position: absolute;
background-color: #ffffff !important;
border: 1px solid rgba(0, 0, 0, 0.15);
border-radius: 0.25rem;
padding: 0.5rem 0;
min-width: 10rem;
z-index: 9999;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.custom-dropdown-menu .dropdown-item {
display: block !important;
width: 100% !important;
padding: 0.5rem 1rem !important;
color: #212529 !important;
text-decoration: none !important;
background-color: transparent !important;
}
.custom-dropdown-menu .dropdown-item:hover {
background-color: #f8f9fa !important;
color: #16181b !important;
cursor: pointer !important;
}
</style>
<!-- End ADD and EDIT Page HTML -->
@ -36,16 +10,16 @@
<div class="col-12">
<div class="card">
<div class="card-body">
<table data-custom-table-css="table" class="table table-sm m-0 table-centered dt-responsive nowrap w-100" cellspacing="a" id="user-table">
<table data-custom-table-css="table" class="table mb-0 nowrap" cellspacing="0" id="user-table">
<thead class="bg-light">
<tr>
<!-- `content` `notes` -->
<th class="font-weight-medium">S.No.</th>
<th class="font-weight-medium">Type</th>
<th class="font-weight-medium">Content Section</th>
<th class="font-weight-medium">Heading</th>
<th class="font-weight-medium">Status</th>
<th class="font-weight-medium">Action</th>
<th class="font-weight-medium">S.No.&nbsp;</th>
<th class="font-weight-medium">Type&nbsp;</th>
<th class="font-weight-medium">Content Section&nbsp;</th>
<th class="font-weight-medium">Heading&nbsp;</th>
<th class="font-weight-medium">Status&nbsp;</th>
<th class="font-weight-medium text-center">Action&nbsp;</th>
</tr>
</thead>
<tbody>
@ -61,10 +35,10 @@
<?php if($row['is_active'] == 1){ echo 'Active'; }else{ echo 'In-Active'; } ?>
</span>
</td>
<td>
<div class="btn-group dropdown" style="position: relative !important;left:0px !important;top:0px !important;">
<td class="text-center table-action-cell">
<div class="btn-group dropdown">
<a href="javascript: void(0);" class="dropdown-toggle arrow-none btn btn-light btn-sm" data-toggle="dropdown" aria-expanded="false"><i class="mdi mdi-dots-horizontal"></i></a>
<div class="dropdown-menu dropdown-menu-right" style="cursor: pointer;">
<div class="dropdown-menu dropdown-menu-right">
<a class="dropdown-item" data-status="<?= $row['is_active']; ?>" data-id="<?= $row['id']; ?>" onclick="handleSaveEditAndDelete('edit', this)">
<i class="mdi mdi-pencil mr-2 text-muted font-18 vertical-middle"></i>Edit
</a>
@ -101,15 +75,30 @@
<div class="form-row">
<div class="col-md-4">
<label for="type">Type<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="type" name="type" placeholder="Enter Type" required>
<input type="text" class="form-control" id="type" name="type" placeholder="Enter Type" required
data-parsley-pattern="^[a-zA-Z0-9_ \-]+$"
data-parsley-pattern-message="Type can contain only letters, numbers, spaces, _ and -"
data-parsley-maxlength="255"
data-parsley-maxlength-message="Type cannot exceed 255 characters"
data-parsley-trigger="keyup input">
</div>
<div class="col-md-4">
<label for="content_section">Content Section<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="content_section" name="content_section" placeholder="Enter Content Section" required>
<input type="text" class="form-control" id="content_section" name="content_section" placeholder="Enter Content Section" required
data-parsley-pattern="^[a-zA-Z0-9_ \-]+$"
data-parsley-pattern-message="Content Section can contain only letters, numbers, spaces, _ and -"
data-parsley-maxlength="255"
data-parsley-maxlength-message="Content Section cannot exceed 255 characters"
data-parsley-trigger="keyup input">
</div>
<div class="col-md-4">
<label for="branch_name">Heading<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="heading" name="heading" placeholder="Enter Heading" required>
<input type="text" class="form-control" id="heading" name="heading" placeholder="Enter Heading" required
data-parsley-pattern="^[a-zA-Z0-9 _\-\.,;:!?()&\/]+$"
data-parsley-pattern-message="Heading can contain only letters, numbers, spaces, and these characters: . , ; : ! ? ( ) & / -"
data-parsley-maxlength="255"
data-parsley-maxlength-message="Heading cannot exceed 255 characters"
data-parsley-trigger="keyup input">
</div>
</div>
<br>
@ -117,21 +106,30 @@
<div class="form-row">
<div class="form-group col-md-12">
<label for="content">Content<span class="text-danger">*</span></label>
<textarea id="content" name="content" class="form-control content" rows="5">
<textarea id="content" name="content" class="form-control content" rows="5" required
data-parsley-maxlength="5000"
data-parsley-maxlength-message="Content cannot exceed 5000 characters"
data-parsley-errors-container="#content-errors">
<!-- <h5>Hello {USER_NAME}, </h5>
<p>We create simple, flat & responsive custom mail template.</p>
<p>Please, write text here!</p> -->
</textarea>
<div id="content-errors"></div>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-12">
<label for="notes">Notes<span class="text-danger">*</span></label>
<textarea id="notes" name="notes" class="form-control notes" rows="5">
<textarea id="notes" name="notes" class="form-control notes" rows="5" required
data-parsley-maxlength="1500"
data-parsley-maxlength-message="Notes cannot exceed 1500 characters"
data-parsley-errors-container="#notes-errors">
<!-- <h5>Hello {USER_NAME}, </h5>
<p>We create simple, flat & responsive custom mail template.</p>
<p>Please, write text here!</p> -->
</textarea>
<div id="notes-errors"></div>
<small id="notes_error" class="text-danger d-none"></small>
</div>
</div>
</div>
@ -259,10 +257,19 @@
// placeholder: "Start typing here...",
// };
content_editor = Jodit.make("#content", editorConfig);
content_editor.events.on('change keyup', function () {
$('#content').val(content_editor.value);
$('#content').parsley().validate();
});
notes_editor = Jodit.make("#notes", editorConfig);
notes_editor.events.on('change keyup', function () {
$('#notes').val(notes_editor.value);
$('#notes').parsley().validate();
});
var ticketsTable = $('#user-table');
ticketsTable.DataTable({
table = ticketsTable.DataTable({
scrollX: true,
// dom: "<'row'<'col-sm-7'f><'col-sm-5 text-right'B>>" + // Filter left, buttons right
// "<'row'<'col-sm-12'tr>>" +
@ -273,7 +280,7 @@
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
buttons: [
{
text: 'Add',
text: '<i class="mdi mdi-plus"></i><span class="btn-custom"> Add </span>',
className: 'btn app-btn-primary mr-2',
action: function(e, dt, node, config) {
resetValues();openModal();// $('#btnSubmit').removeClass('d-none');
@ -306,12 +313,34 @@
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666; display:none;"></i>
</div>`,
searchPlaceholder: "Search",
emptyTable: '<div class="text-center text-muted">No Data found</div>'
emptyTable: '<div class="text-center text-muted">No Data found</div>',
paginate: {
previous: '◄',
next: '►'
}
},
paging: true, // Enable pagination
pageLength: 10, // Set default number of rows per page (optional)
// ordering: false,
});
function applyBottomRowDropup() {
if (!table) return;
const currentRows = table.rows({ page: 'current' }).nodes().toArray();
$('#user-table tbody tr').removeClass('nh-force-dropup');
$('#user-table tbody tr td.table-action-cell .btn-group.dropdown').removeClass('dropup');
const targetCount = Math.min(2, currentRows.length);
for (let i = 0; i < targetCount; i++) {
const row = currentRows[currentRows.length - 1 - i];
if (!row) continue;
$(row).addClass('nh-force-dropup');
$(row).find('td.table-action-cell .btn-group.dropdown').addClass('dropup');
}
}
applyBottomRowDropup();
$('#user-table').on('draw.dt', applyBottomRowDropup);
});
$('.close').click(function(){ resetValues(); })
@ -560,6 +589,20 @@
return;
}
// Sync jodit value back to textarea before parsely validation
if (typeof content_editor !== 'undefined') {
$('#content').val(content_editor.value);
}
if (typeof notes_editor !== 'undefined') {
$('#notes').val(notes_editor.value);
}
var isValid = $(this).parsley({
excluded: 'input[type=button], input[type=submit], input[type=reset], input[type=hidden]'
}).validate();
if (!isValid) return;
// Content validation
if (typeof content_editor !== 'undefined') {
if (isEditorEmpty(content_editor.value)) {
@ -629,9 +672,24 @@
}
});
// Restrict user typing to allow Regex only characters
$('#type').on('input', function() {
this.value = this.value.replace(/[^a-zA-Z0-9_ \-]/g, '');
});
$('#content_section').on('input', function() {
this.value = this.value.replace(/[^a-zA-Z0-9_ \-]/g, '');
});
$('#heading').on('input', function() {
this.value = this.value.replace(/[^a-zA-Z0-9 _\-.,;:!?()&\/]/g, '');
});
// $('#type').on('input blur', function () { fieldTouched.type = true; validateTypeField(false); });
// $('#heading').on('input blur', function () { fieldTouched.heading = true; validateHeadingField(false); });
</script>
<script>
<!-- <script>
document.addEventListener("DOMContentLoaded", function() {
const table = document.getElementById("user-table");
@ -720,4 +778,4 @@
});
});
</script>
-->

View File

@ -49,6 +49,20 @@ table.dataTable tbody td {
}
.dataTables_length label {height: 21px !important;}
#scroll-horizontal-datatable_wrapper .datatable-search-wrapper{
width: auto !important;
}
#scroll-horizontal-datatable_wrapper .dataTables_filter input{
width: 220px !important;
padding-right: 34px !important;
}
#scroll-horizontal-datatable_wrapper .datatable-search-icon,
#scroll-horizontal-datatable_wrapper .datatable-clear-icon{
right: 11px !important;
}
</style>
@ -109,13 +123,13 @@ $(document).ready(function () {
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
language: {
search: `
<div class="datatable-search-wrapper" style="position:relative; display:inline-block; width:100%;">
<div class="datatable-search-wrapper" style="position:relative; display:inline-block;">
_INPUT_
<i class="mdi mdi-magnify datatable-search-icon"
style="position:absolute; right:122px !important; top:50%; transform:translateY(-50%); color:#666;"></i>
style="position:absolute; right:11px; top:50%; transform:translateY(-53%); color:#666;"></i>
<i class="mdi mdi-close-circle datatable-clear-icon"
style="position:absolute; right:122px !important; top:50%; transform:translateY(-50%); color:#666; display:none; cursor:pointer;"></i>
style="position:absolute; right:11px; top:50%; transform:translateY(-53%); color:#666; display:none; cursor:pointer;"></i>
</div>
`,
searchPlaceholder: "Search",
@ -146,10 +160,8 @@ $('.dataTables_filter input').css({
'background': '#F0F0F0',
'border': 'none',
'border-radius': '8px',
'padding-right': '35px'
}).wrap('<div style="position:relative; display:inline-block;"></div>')
.after('<i class="mdi mdi-magnify" ' +
'style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666;"></i>');
'padding-right': '34px'
});
// Attach submit handler separately so it works

View File

@ -12,9 +12,7 @@ table.dataTable thead th {
padding: 4px 4px !important;
}
.dataTables_filter {
position: absolute;
}
/* .dataTables_filter: global listing layout (nhance-datatables-uniform.css) */
.filter-sidebar {
height: 100%;

View File

@ -69,7 +69,7 @@ input:checked + .slider:before {
<div class="col-12">
<div class="card-body">
<div class="custom-form">
<form role="form" class="parsley-examples" method="post" id="insurer_general_form" enctype="multipart/form-data">
<form role="form" class="parsley-examples" method="post" id="insurer_general_form" enctype="multipart/form-data" data-parsley-validate>
<input type="hidden" value="<?= csrf_hash() ?>" name="<?= csrf_token() ?>" />
<input type="hidden" name="PrimaryKey" id="insurer_General_PrimaryKey" value="<?= isset($insurer['id']) ? $insurer['id'] : '' ?>" />
<input type="hidden" name="insurer_id" id="insurer_id" />
@ -78,8 +78,12 @@ input:checked + .slider:before {
<div class="form-group row">
<label for="insurer_name" class="col-md-4 col-form-label">Insurer Name<span class="text-danger">*</span></label>
<div class="col-md-5">
<input type="text" class="form-control" id="insurer_name" placeholder="Enter Insurer Name"
value="<?= isset($insurer['name']) ? $insurer['name'] : '' ?>" name="name" required>
<input type="text" class="form-control" id="insurer_name" placeholder="Enter Insurer Name"
value="<?= isset($insurer['name']) ? $insurer['name'] : '' ?>" name="name"
required
data-parsley-pattern="/^[a-zA-Z0-9\s\-_]+$/"
data-parsley-pattern-message="Insurer Name can only contain letters, numbers, spaces, hyphens, and underscores."
data-parsley-trigger="keyup">
</div>
</div>
@ -87,8 +91,12 @@ input:checked + .slider:before {
<div class="form-group row">
<label for="short_name" class="col-md-4 col-form-label">Insurer Short Name<span class="text-danger">*</span></label>
<div class="col-md-5">
<input type="text" class="form-control" id="short_name" placeholder="Enter Short Name"
value="<?= isset($insurer['short_name']) ? $insurer['short_name'] : '' ?>" name="short_name" required minlength="3" maxlength="8">
<input type="text" class="form-control" id="short_name" placeholder="Enter Short Name"
value="<?= isset($insurer['short_name']) ? $insurer['short_name'] : '' ?>" name="short_name"
required data-parsley-minlength="3" data-parsley-maxlength="8"
data-parsley-pattern="/^[a-zA-Z0-9\-_]+$/"
data-parsley-pattern-message="Short Name can only contain letters, numbers, hyphens, and underscores (no spaces)."
data-parsley-trigger="keyup">
</div>
</div>
@ -160,10 +168,15 @@ input:checked + .slider:before {
<label for="insurer_logo" class="col-md-4 col-form-label">Insurer Logo</label>
<div class="col-md-5">
<div class="input-icon">
<input type="file" class="form-control" id="insurer_logo" name="insurer_logo"
accept="image/jpeg, image/jpg, image/png" onchange="PreviewImage();">
<input type="file" class="form-control" id="insurer_logo" name="insurer_logo"
accept="image/jpeg, image/jpg, image/png" onchange="PreviewImage();"
data-parsley-max-file-size="200"
data-parsley-file-extension="jpg,jpeg,png"
data-parsley-errors-container="#logo_error_container">
<i class="mdi mdi-upload additional-icon"></i>
</div>
<div id="logo_error_container"></div>
<small class="text-muted">Allowed: JPG, JPEG, PNG. Max size: 200KB.</small>
</div>
<div class="col-md-3">
<img src="<?= isset($insurer['insurer_logo']) && !empty($insurer['insurer_logo']) ? base_url() . "public/uploads/logo/" . $insurer['insurer_logo'] : base_url() . "public/assets/images/avatar_2x.png" ?>"
@ -191,6 +204,40 @@ input:checked + .slider:before {
<script>
$(document).ready(function() {
// Custom Parsley validator for file size
window.Parsley.addValidator('maxFileSize', {
validateString: function(value, maxSize, parsleyInstance) {
if (!window.FileReader) {
alert('Your browser does not support FileReader API, please upgrade.');
return true;
}
var files = parsleyInstance.$element[0].files;
if (files.length == 0) {
return true; // No file, so validation passes (unless required)
}
return files[0].size <= maxSize * 1024;
},
requirementType: 'integer',
messages: {
en: 'This file should not be larger than %s KB.'
}
});
// Custom Parsley validator for file extension
window.Parsley.addValidator('fileExtension', {
validateString: function(value, requirement, parsleyInstance) {
var file = parsleyInstance.$element[0].files[0];
if (!file) { return true; } // No file, so validation passes
var extensions = requirement.split(',').map(function(ext) { return ext.trim().toLowerCase(); });
var fileExtension = file.name.split('.').pop().toLowerCase();
return extensions.indexOf(fileExtension) !== -1;
},
requirementType: 'string',
messages: {
en: 'Allowed extensions are: %s.'
}
});
$('#insurer_logo').on('change', function() {
PreviewImage();
});
@ -293,34 +340,6 @@ input:checked + .slider:before {
}
});
function validateForm() {
var isValid = true;
// Perform validation for each field
$('#insurer_general_form input, #insurer_general_form select').each(function() {
// Check for empty text inputs and selects
if ($(this).is('input[type="text"]') || $(this).is('select')) {
if ($.trim($(this).val()) == '') {
alert('Please fill in all fields');
isValid = false;
return false; // Exit the loop early
}
}
// Check for file inputs (assuming image files)
if ($(this).is('input[type="file"]')) {
var fileInput = $(this)[0];
if (fileInput.files.length === 0) {
alert('Please select an image file');
isValid = false;
return false; // Exit the loop early
}
}
});
return isValid;
}
});

View File

@ -28,17 +28,31 @@
overflow-y: auto;
max-height: 80vh;
}
#insurerBranchModal {
padding-right: 0 !important;
}
#insurerBranchModal.show {
display: flex !important;
align-items: center;
justify-content: center;
}
#insurerBranchModal {
z-index: 1060;
}
.modal-backdrop {
z-index: 1050;
}
</style>
<div class="tab-pane fade" id="branch-tab" style="padding-left: 25px;">
<div class="row float-right" style="padding-bottom: 10px;position: relative;">
<a href="#" id="btnBranchAdd" class="btn app-btn-primary mr-2"><span class="mdi mdi-plus" aria-hidden="true" style="padding: 5px 10px;"></span>Add Branch</a>
</div>
<div class="table-responsive" id="branch_table" >
<div class="table-responsive nh-listing-table-scope" id="branch_table" >
<table data-custom-table-css="table" class="table table-borderless table-nowrap mb-0" id="branch-datatable">
<table data-custom-table-css="table" class="table table-borderless table-nowrap mb-0 w-100 nowrap" id="branch-datatable">
<thead class="">
<tr>
@ -52,137 +66,22 @@
</table>
</div>
<!-- <div class="row" id="add_branch">
<div class="col-12">
<div class="card-body">
<div class="row float-right" style="position: relative; bottom: 20px; right: 13px;">
<button type="button" id="btnBranchBack" class="btn btn-primary waves-effect waves-light btn-sm btnBack"><span class="mdi mdi-format-list-bulleted" aria-hidden="true" style="padding: 5px 10px;"></span>Back To List</button>
</div>
<hr>
<form role="form" class="parsley-examples" method="post" id="insurer_branch_form"
enctype="multipart/form-data">
<input type="hidden" class="form-control" value="<?= csrf_hash() ?>" name="<?= csrf_token() ?>" />
<input type="hidden" name="PrimaryKey" id="insurer_Branch_PrimaryKey" />
<input type="hidden" name="insurer_id" id="insurer_id_branch" value="<?= isset($insurer['id']) ? $insurer['id'] : '' ?>"/>
<div class="form-group">
<div class="form-row">
<div class="form-group col-md-6">
<label for="branch_name">Branch Name<span
class="text-danger">*</span></label>
<input type="text" class="form-control" id="branch_name"
placeholder="Enter Branch Name" name="branch_name" required>
</div>
<div class="form-group col-md-6">
<label for="branch_code">Branch Code<span
class="text-danger">*</span></label>
<input type="text" class="form-control" id="branch_code"
placeholder="Enter Branch Code" name="branch_code" required>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<label for="address1">Address Line 1<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="address1" name="address1" placeholder="Enter Address 1" required>
</div>
<div class="form-group col-md-6">
<label for="address2">Address Line 2<span class="text-danger"></span></label>
<input type="text" class="form-control" id="address2" placeholder="Enter Address 2" name="address2">
</div>
</div>
<div class="form-row">
<div class="form-group col-md-3">
<label for="state">State<span
class="text-danger">*</span></label>
<select class="form-control" id="state" name="state" required>
<option value="">Select State</option>
<?php foreach($state as $value) { ?>
<option value="<?= $value['id'] ?>"><?= $value['state'] ?></option>
<?php } ?>
</select>
</div>
<div class="form-group col-md-3">
<label for="district">District<span
class="text-danger">*</span></label>
<input type="text" class="form-control" id="district"
placeholder="Enter District" name="district" required>
</div>
<div class="form-group col-md-3">
<label for="city">City<span
class="text-danger">*</span></label>
<input type="text" class="form-control" id="branch_city"
placeholder="Enter City" name="city" required>
</div>
<div class="form-group col-md-3">
<label for="gst">Pincode<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="pincode" placeholder="Enter Pincode" name="pincode" required>
</div>
</div>
<hr>
<h6 class="header-title">Contact 1</h6>
<br>
<div class="form-row">
<input type="hidden" name="primarykey[]" id="contact_id_1" value=""/>
<div class="form-group col-md-6">
<label for="first_name">Name<span class="text-danger">*</span></label>
<input value="" type="text" class="form-control" placeholder="Enter Contact Name" name="name[]" id="name" required>
</div>
<div class="form-group col-md-6">
<label for="designation">Designation<span class="text-danger">*</span></label>
<input value="" type="text" class="form-control" placeholder="Enter Designation Name" name="designation[]" id="designation" required>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<label for="last_name">Email<span class="text-danger">*</span></label>
<input value="" type="email" data-parsley-type="email" class="form-control" placeholder="Enter Contact Email" name="email[]" id="email" required>
</div>
<div class="form-group col-md-6">
<label for="mobile">Mobile<span class="text-danger">*</span></label>
<input name="mobile[]" pattern="[0-9]{10}" value="" type="tel" data-parsley-type="number" data-parsley-length="[10,10]" class="form-control" placeholder="Enter Contact Mobile" id="mobile" required>
</div>
</div>
<div class="form-group" style="display: flex;">
<button style="margin-right: 10px;" type="button" class="btn btn-primary btn-sm ac" onclick="appendContactHtml()" id="add">Add Contact</button>
<button type="button" class="btn btn-danger btn-sm" onclick="removeContact(this)" id="${uniqueId}_remove">Remove</button>
</div>
<div id="container"></div>
</div>
<div class="form-group text-right m-b-0">
<button type="submit" class="btn btn-primary waves-effect waves-light mr-1"
id="btnSubmit">Submit</button>
<button type="button" class="btn btn-secondary waves-effect btnBack"
id="btnBack">Cancel</button>
</div>
</form>
</div>
</div>
</div> !-- end col-- -->
</div>
<!-- end -->
<!-- Modal -->
<div class="modal fade" id="insurerBranchModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-xl" style="max-width: 1140px !important;">
<div class="modal-dialog modal-dialog-centered modal-xl">
<div class="modal-content">
<!-- Modal Header -->
<div class="modal-header flex-column">
<div class="d-flex w-100 justify-content-between align-items-center">
<h4 class="modal-title mb-0" id="modalLabel">New Branch</h4>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<button type="button" class="close js-branch-modal-close" data-dismiss="modal" data-bs-dismiss="modal" aria-hidden="true">×</button>
</div>
</div>
<!-- Modal Body -->
<div class="modal-body" style="overflow-y:auto; max-height:80vh;">
<div class="modal-body">
<form id="insurer_branch_form" role="form" class="parsley-examples" method="post" action="" enctype="multipart/form-data">
<input type="hidden" class="form-control" value="<?= csrf_hash() ?>" name="<?= csrf_token() ?>" />
<input type="hidden" name="PrimaryKey" id="insurer_Branch_PrimaryKey" />
@ -253,7 +152,7 @@
<!-- <div class="form-group col-md-12 mt-2"> -->
<div style="margin-top: 85px;">
<button type="submit" class="btn btn-primary mr-2" id="btnSubmit">Submit</button>
<button type="button" class="btn app-btn-outline-secondary btnBack" id="btnBack" data-dismiss="modal" aria-hidden="true">Cancel</button>
<button type="button" class="btn app-btn-outline-secondary btnBack" id="btnBack" data-dismiss="modal" data-bs-dismiss="modal" aria-hidden="true">Cancel</button>
</div>
<!-- </div> -->
</div>
@ -323,113 +222,296 @@
var contactCount = 1;
var insurer_branch_form_action = '';
var insurer_Branch_PrimaryKey = $('#insurer_id_branch').val();
var branchDataTable = null;
var branchDebugPrefix = '[InsurerBranch]';
window.addEventListener('error', function (event) {
try {
console.error(branchDebugPrefix + ' window.error:', event.message, 'at', event.filename + ':' + event.lineno + ':' + event.colno, event.error || '');
} catch (e) {}
});
window.addEventListener('unhandledrejection', function (event) {
try {
console.error(branchDebugPrefix + ' unhandledrejection:', event.reason);
} catch (e) {}
});
/**
* This app loads Bootstrap 3/4 jQuery plugins (see footer) and may also expose
* a partial window.bootstrap.Modal. Prefer $.fn.modal when present so the modal
* actually opens; native bootstrap.Modal is only a fallback.
*/
function showBranchModal() {
var $m = $('#insurerBranchModal');
console.log(branchDebugPrefix + ' modal function');
if (typeof jQuery !== 'undefined' && jQuery.fn && typeof jQuery.fn.modal === 'function') {
try {
$m.modal('show');
console.log(branchDebugPrefix + ' opened with jQuery.modal');
setTimeout(function () {
if (!$m.hasClass('show') || !$m.is(':visible')) {
console.warn(branchDebugPrefix + ' jQuery modal did not render, applying force-open fallback');
forceShowBranchModal();
}
}, 120);
} catch (err) {
console.error(branchDebugPrefix + ' jQuery.modal show failed:', err);
forceShowBranchModal();
}
return;
}
var modalEl = document.getElementById('insurerBranchModal');
if (!modalEl) {
console.error(branchDebugPrefix + ' modal element #insurerBranchModal not found');
return;
}
if (window.bootstrap && bootstrap.Modal) {
if (typeof bootstrap.Modal.getOrCreateInstance === 'function') {
try {
bootstrap.Modal.getOrCreateInstance(modalEl).show();
console.log(branchDebugPrefix + ' opened with bootstrap.Modal.getOrCreateInstance');
} catch (err) {
console.error(branchDebugPrefix + ' bootstrap getOrCreateInstance failed:', err);
forceShowBranchModal();
}
return;
}
if (typeof bootstrap.Modal.getInstance === 'function') {
var existing = bootstrap.Modal.getInstance(modalEl);
if (existing) {
try {
existing.show();
console.log(branchDebugPrefix + ' opened with existing bootstrap modal instance');
} catch (err) {
console.error(branchDebugPrefix + ' existing bootstrap modal show failed:', err);
forceShowBranchModal();
}
return;
}
}
try {
(new bootstrap.Modal(modalEl)).show();
console.log(branchDebugPrefix + ' opened with new bootstrap.Modal');
} catch (e) {
console.error(branchDebugPrefix + ' bootstrap.Modal constructor show failed:', e);
forceShowBranchModal();
}
return;
}
console.error(branchDebugPrefix + ' no modal library available (jQuery modal/bootstrap modal missing)');
forceShowBranchModal();
}
function forceShowBranchModal() {
var modalEl = document.getElementById('insurerBranchModal');
if (!modalEl) {
console.error(branchDebugPrefix + ' forceShow failed: modal element missing');
return;
}
modalEl.style.display = 'block';
modalEl.classList.add('show');
modalEl.removeAttribute('aria-hidden');
modalEl.setAttribute('aria-modal', 'true');
document.body.classList.add('modal-open');
if (!document.querySelector('.modal-backdrop')) {
var backdrop = document.createElement('div');
backdrop.className = 'modal-backdrop fade show';
document.body.appendChild(backdrop);
}
console.log(branchDebugPrefix + ' force-open fallback applied');
}
function hideBranchModal() {
var $m = $('#insurerBranchModal');
if (typeof jQuery !== 'undefined' && jQuery.fn && typeof jQuery.fn.modal === 'function') {
$m.modal('hide');
return;
}
var modalEl = document.getElementById('insurerBranchModal');
if (window.bootstrap && bootstrap.Modal) {
if (typeof bootstrap.Modal.getOrCreateInstance === 'function') {
bootstrap.Modal.getOrCreateInstance(modalEl).hide();
return;
}
if (typeof bootstrap.Modal.getInstance === 'function') {
var existing = bootstrap.Modal.getInstance(modalEl);
if (existing) {
existing.hide();
return;
}
}
try {
(new bootstrap.Modal(modalEl)).hide();
} catch (e) {}
}
}
function resetInsurerBranchFormState() {
var $form = $('#insurer_branch_form');
if ($form.length && $form[0] && typeof $form[0].reset === 'function') {
$form[0].reset();
}
if ($form.length && typeof $form.parsley === 'function') {
try {
$form.parsley().reset();
} catch (e) {}
}
contactCount = 1;
$('#container').empty();
$('#contact_1_wrapper input').val('');
$('#insurer_Branch_PrimaryKey').val('');
toggleAddButton();
}
function renderBranchRows(rows) {
var html = '';
$.each(rows || [], function(index, item) {
html += `
<tr>
<td>${item.branch_name || ''}</td>
<td>${item.branch_code || ''}</td>
<td>
<a class="mdi mdi-lead-pencil btnBranchEdit" href="#" data-id="${item.id}" style="font-size:18px;"></a>
<a style="color:#02a8b5" data-id="${item.id}" onclick="removeInsurerBranch(this)"><i data-id="${item.id}" class="mdi mdi-delete" style="font-size:18px;"></i></a>
</td>
</tr>
`;
});
$('#insurer_branch_list').html(html);
}
function initBranchDataTable() {
if ($.fn.DataTable.isDataTable('#branch-datatable')) {
$('#branch-datatable').DataTable().destroy();
}
branchDataTable = $('#branch-datatable').DataTable({
paging: true,
searching: true,
autoWidth: false,
scrollX: false,
responsive: false,
dom: "<'row'<'col-12 d-flex justify-content-between align-items-center'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'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>>",
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
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:11px; top:50%; transform:translateY(-53%); color:#666;"></i>
<i class="mdi mdi-close-circle datatable-clear-icon" style="position:absolute; right:11px; top:50%; transform:translateY(-53%); color:#666; display:none;"></i>
</div>`,
searchPlaceholder: "Search",
emptyTable: '<div class="text-center text-muted">No data available</div>'
},
buttons: [
{
text: '<i class="mdi mdi-plus" ></i><span class=" btn-custom"> Add Branch </span>',
className: 'btn app-btn-primary mr-2 js-insurer-branch-add',
action: function(e, dt, node, config) {
openCreateBranchModal(e);
}
}
]
});
}
function openCreateBranchModal(e) {
try {
console.log(branchDebugPrefix + ' add button click captured');
if (e && typeof e.preventDefault === 'function') {
e.preventDefault();
}
insurer_branch_form_action = '<?= base_url("master/insurer/branch/create"); ?>';
contactCount = 1;
showBranchModal();
var $form = $('#insurer_branch_form');
if ($form.length && $form[0] && typeof $form[0].reset === 'function') {
$form[0].reset();
}
$('#branch_name').val('');
$('#branch_code').val('');
$('#state').val('');
$('#district').val('');
$('#branch_city').val('');
$('#name').val('');
$('#email').val('');
$('#mobile').val('');
$('#designation').val('');
var addButton = document.getElementById('add');
if (addButton && addButton.style.display === 'none') {
addButton.style.display = 'block';
}
var buttonIds = JSON.parse(localStorage.getItem('buttonIds')) || [];
for (var i = 0; i < buttonIds.length; i++) {
var firstElement = buttonIds[i].split('_')[0];
var elementToRemove = document.getElementById(firstElement);
if (elementToRemove) {
elementToRemove.parentNode.removeChild(elementToRemove);
}
localStorage.removeItem('buttonIds');
}
} catch (err) {
console.error(branchDebugPrefix + ' openCreateBranchModal failed:', err);
}
}
$(document).ready(function () {
insurer_Branch_PrimaryKey = $('#insurer_id_branch').val();
var myModal = new bootstrap.Modal(document.getElementById('insurerBranchModal'));
myModal.hide();
// $('#add_branch').hide();
// $('.btnBack').hide();
var data = <?= isset($insuer_branch) ? json_encode($insuer_branch) : '[]' ?>;
if(insurer_Branch_PrimaryKey !== ''){
var branchTable = '';
var data = <?= isset($insuer_branch) ? json_encode($insuer_branch) : '[]' ?>;
console.log("insurer branch data", data);
$.each(data, function(index, item) {
branchTable += `
<tr>
<td>${item.branch_name}</td>
<td>${item.branch_code}</td>
<td>
<a class="mdi mdi-lead-pencil btnBranchEdit" href="#" data-id="${item.id}" style="font-size:18px;"></a>
<a style="color:#02a8b5" data-id="${item.id}" onclick="removeInsurerBranch(this)"><i data-id="${item.id}" class="mdi mdi-delete" style="font-size:18px;"></i></a>
</td>
</tr>
`;
});
$('#insurer_branch_list').append(branchTable);
}
renderBranchRows(data);
initBranchDataTable();
$('#branch-datatable').DataTable({
paging: true ,
searching: true
});
});
$('#btnBranchAdd').click(function(){
$("#insurer_branch_form")[0].reset();
contactCount = 1
insurer_branch_form_action = '<?= base_url("master/insurer/branch/create"); ?>';
var myModal = new bootstrap.Modal(document.getElementById('insurerBranchModal'));
myModal.show();
// $('#add_branch').show();
// $('#branch_table').hide();
// $('.btnBack').show();
// $('#btnBranchAdd').hide();
$('#branch_name').val('');
$('#branch_code').val('');
$('#state').val('');
$('#district').val('');
$('#branch_city').val('');
$('#name').val('');
$('#email').val('');
$('#mobile').val('');
$('#designation').val('');
contactCount = 1
var addButton = document.getElementById('add');
if (addButton.style.display === 'none') {
addButton.style.display = 'block';
}
var buttonIds = JSON.parse(localStorage.getItem('buttonIds')) || [];
for (var i = 0; i < buttonIds.length; i++) {
var firstElement = buttonIds[i].split('_')[0];
console.log(firstElement);
// Find the element by its ID
var elementToRemove = document.getElementById(firstElement);
console.log(elementToRemove);
if (elementToRemove) {
console.log(elementToRemove);
elementToRemove.parentNode.removeChild(elementToRemove);
$('a[data-toggle="tab"], a[data-bs-toggle="tab"]').on('shown.bs.tab', function () {
if ($.fn.DataTable.isDataTable('#branch-datatable')) {
$('#branch-datatable').DataTable().columns.adjust();
}
localStorage.removeItem('buttonIds')
});
$(document).on('click', '.js-insurer-branch-add', function(e){
console.log(branchDebugPrefix + ' delegated click fired for .js-insurer-branch-add/#btnBranchAdd');
// DataTable button already calls openCreateBranchModal in its action.
// Avoid double invocation when both handlers fire.
if (!$(this).hasClass('dt-button')) {
openCreateBranchModal(e);
}
});
$('#insurerBranchModal').on('hidden.bs.modal', function () {
resetInsurerBranchFormState();
});
});
$('.btnBack').click(function(e){
if (e && typeof e.preventDefault === 'function') {
e.preventDefault();
}
})
hideBranchModal();
});
$('.btnBack').click(function(){
$("#insurer_branch_form")[0].reset();
contactCount = 1
$('.close').click();
// $('#add_branch').hide();
// $('#branch_table').show();
// $('.btnBack').hide();
// $('#btnBranchAdd').show();
$('#branch_name').val('');
$('#branch_code').val('');
$('#state').val('');
$('#district').val('');
$('#branch_city').val('');
$('#name').val('');
$('#email').val('');
$('#mobile').val('');
$('#designation').val('');
$('#container').empty(); $('#contact_1_wrapper input').val('');
})
$(document).on('click', '.js-branch-modal-close', function(e){
if (e && typeof e.preventDefault === 'function') {
e.preventDefault();
}
hideBranchModal();
});
$("#insurer_branch_form").submit(function(events) {
@ -457,23 +539,10 @@
$("#insurer_branch_form")[0].reset();
console.log(res);
$('#insurer_branch_list tr').remove();
var insurerBranchTableInsert = ''
$.each(res.data, function(index, item) {
insurerBranchTableInsert += `
<tr>
<td>${item.branch_name}</td>
<td>${item.branch_code}</td>
<td>
<a class="mdi mdi-lead-pencil btnBranchEdit" href="#" data-id="${item.id}" style="font-size:18px;"></a>
<a style="color:#02a8b5" data-id="${item.id}" onclick="removeInsurerBranch(this)"><i data-id="${item.id}" class="mdi mdi-delete" style="font-size:18px;"></i></a>
</td>
</tr>
`;
});
$('#insurer_branch_list').append(insurerBranchTableInsert);
renderBranchRows(res.data || []);
initBranchDataTable();
$('#container').empty(); $('#contact_1_wrapper input').val('');
$('.close').click();
hideBranchModal();
// window.location.reload();
// $('#add_branch').hide();
// $('#branch_table').show();
@ -496,7 +565,7 @@
$('.loader').fadeOut();
$('.loader-mask').delay(350).fadeOut('slow');
}, 1000);
$('.close').click();
hideBranchModal();
}
});
@ -527,8 +596,7 @@
$('.loader').fadeOut();
$('.loader-mask').delay(350).fadeOut('slow');
}, 1000);
var myModal = new bootstrap.Modal(document.getElementById('insurerBranchModal'));
myModal.show();
showBranchModal();
// $('#add_branch').show();
// $('#branch_table').show();
// $('.btnBack').show();

View File

@ -1,4 +1,8 @@
<style>
#insurer_datatable {
width: 100% !important;
}
.table-responsive {
overflow-x: auto;
}
@ -62,13 +66,13 @@
<div class="form-group col-md-6" style="position: relative;top: 28px;">
<label for="email"><span class="text-danger"></span></label>
<a href="#" class="btn app-btn-secondary mr-2" onclick="checkInsurerTemplete(this)">Copy</a>
<a href="javascript:void(0);" class="btn app-btn-secondary mr-2" onclick="checkInsurerTemplete(this)">Copy</a>
</div>
<div class="form-group col-md-2" style="position: relative;top: 28px;">
<div class="float-right">
<label for="email"><span class="text-danger"></span></label>
<a href="#" class="btn app-btn-primary " onclick="addNewJsonExportTemplate(this)"><i class="mdi mdi-plus"></i> Add </a>
<a href="javascript:void(0);" class="btn app-btn-primary" onclick="addNewJsonExportTemplate(this)"><i class="mdi mdi-plus"></i> Add </a>
</div>
</div>
@ -76,7 +80,7 @@
<div class="table-responsive" id="branch_table" >
<table data-custom-table-css="table" class="table table-borderless table-nowrap mb-0" id="insurer_datatable">
<table data-custom-table-css="table" class="table table-borderless table-nowrap mb-0 w-100" id="insurer_datatable">
<thead class="">
<tr>
@ -102,7 +106,7 @@
<a href="javascript: void(0);" class="dropdown-toggle arrow-none btn btn-light btn-sm" data-toggle="dropdown" aria-expanded="false"><i class="mdi mdi-dots-horizontal"></i></a>
<div class="dropdown-menu dropdown-menu-right">
<a class="dropdown-item" href="#" onclick="editJSONExportTemplate(this, '<?= $value['id']; ?>')"><i class="mdi mdi-pencil mr-2 text-muted font-18 vertical-middle"></i>Edit</a>
<a class="dropdown-item" id="template_id_for_duplicate" data-id="<?= $value['id']; ?>" data-toggle="modal" data-target="#centermodal"><i class="mdi mdi-content-duplicate mr-2 text-muted font-18 vertical-middle"></i>Duplicate</a>
<a class="dropdown-item template_id_for_duplicate" data-id="<?= $value['id']; ?>" data-toggle="modal" data-target="#centermodal"><i class="mdi mdi-content-duplicate mr-2 text-muted font-18 vertical-middle"></i>Duplicate</a>
</div>
</div>
</td>
@ -213,6 +217,7 @@
<div class="modal-body">
<input type="hidden" id="insurer_id_for_dub_temp" value="<?= isset($insurer['id']) ? $insurer['id'] : '' ?>">
<input type="hidden" id="duplicate_template_id" value="">
<div class="form-row">
@ -254,18 +259,59 @@
</div><!-- /.modal -->
<script>
var insurerTemplateDataTable = null;
function showTemplateModal() {
var modalEl = document.getElementById('template_modal');
if (window.bootstrap && bootstrap.Modal) {
if (typeof bootstrap.Modal.getOrCreateInstance === 'function') {
bootstrap.Modal.getOrCreateInstance(modalEl).show();
return;
}
if (typeof bootstrap.Modal.getInstance === 'function') {
var existing = bootstrap.Modal.getInstance(modalEl);
if (existing) {
existing.show();
return;
}
}
try {
(new bootstrap.Modal(modalEl)).show();
return;
} catch (e) {}
}
$('#template_modal').modal('show');
}
function initInsurerTemplateDataTable() {
if ($.fn.DataTable.isDataTable('#insurer_datatable')) {
$('#insurer_datatable').DataTable().destroy();
}
insurerTemplateDataTable = $('#insurer_datatable').DataTable({
paging: true,
searching: true,
autoWidth: false,
scrollX: false,
responsive: false,
language: {
emptyTable: "No template data available"
}
});
}
$(document).ready(function(){
$('#insurer').select2();
// $('#event').select2();
featchInsurerList();
setTimeout(() => {
$('#insurer_datatable').DataTable({
paging: true ,
searching: true
});
}, 2000);
initInsurerTemplateDataTable();
$('a[data-toggle="tab"], a[data-bs-toggle="tab"]').on('shown.bs.tab', function () {
if ($.fn.DataTable.isDataTable('#insurer_datatable')) {
$('#insurer_datatable').DataTable().columns.adjust();
}
});
$(document).on('click', '.template_id_for_duplicate', function() {
$('#duplicate_template_id').val($(this).data('id'));
});
})
@ -488,11 +534,13 @@
function addNewJsonExportTemplate(input)
{
if (input && input.preventDefault) {
input.preventDefault();
}
$('#myCenterModalLabel').text('Create Template');
$('#dynamic-form-container').empty();
addHTMLInput();
var myModal = new bootstrap.Modal(document.getElementById('template_modal'));
myModal.show();
showTemplateModal();
}
function addHTMLInput(element = null, data = null)
@ -658,8 +706,7 @@
}
});
var myModal = new bootstrap.Modal(document.getElementById('template_modal'));
myModal.show();
showTemplateModal();
}
function checkForDuplicates(selectElement)
@ -693,10 +740,15 @@
function dublicateTemplate()
{
var template_id = $('#template_id_for_duplicate').attr('data-id');
var template_id = $('#duplicate_template_id').val();
var event_name = $('#event_name_for_duplicate').val();
var insurer_id = $('#insurer_id_for_dub_temp').val();
if (template_id === "") {
toastr.warning('Please select a template to duplicate.', 'Warning');
return false;
}
console.log('template_id_for_duplicate', template_id)
console.log('event_name_for_duplicate', event_name)
console.log('insurer_id', insurer_id)

View File

@ -1,31 +1,4 @@
<style>
.custom-dropdown-menu {
display: none;
position: absolute;
background-color: #ffffff !important;
border: 1px solid rgba(0, 0, 0, 0.15);
border-radius: 0.25rem;
padding: 0.5rem 0;
min-width: 10rem;
z-index: 9999;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.custom-dropdown-menu .dropdown-item {
display: block !important;
width: 100% !important;
padding: 0.5rem 1rem !important;
color: #212529 !important;
text-decoration: none !important;
background-color: transparent !important;
}
.custom-dropdown-menu .dropdown-item:hover {
background-color: #f8f9fa !important;
color: #16181b !important;
cursor: pointer !important;
}
.btn-color {
background-color: rgba(52, 174, 178, 1);
color: white;
@ -40,6 +13,10 @@
.dataTables_length label {height: 21px !important;}
.col-12 {
max-width: 98% !important;
}
</style>
<div class="container-fluid-min">
@ -52,13 +29,13 @@
<h4 style="position: relative;">Insurers</h4>
</div>
</div> -->
<table data-custom-table-css="table" class="table table-sm table-hover m-0 table-centered dt-responsive nowrap w-100" cellspacing="0"
<table data-custom-table-css="table" class="table mb-0 nowrap" cellspacing="0"
id="tickets-table">
<thead class="bg-light">
<tr>
<th class="font-weight-medium">Name</th>
<th class="font-weight-medium">Type</th>
<th class="font-weight-medium">Action</th>
<th class="font-weight-medium">Name&nbsp;</th>
<th class="font-weight-medium">Type&nbsp;</th>
<th class="font-weight-medium text-center">Action&nbsp;</th>
</tr>
</thead>
@ -68,7 +45,7 @@
<tr>
<td><?php echo $row['name']; ?> ( <?php echo $row['short_name']; ?> ) </td>
<td><?php echo $row['type']; ?></td>
<td>
<td class="text-center table-action-cell">
<div class="btn-group dropdown">
<a href="javascript: void(0);" class="dropdown-toggle arrow-none btn btn-light btn-sm" data-toggle="dropdown" aria-expanded="false"><i class="mdi mdi-dots-horizontal"></i></a>
<div class="dropdown-menu dropdown-menu-right">
@ -91,100 +68,10 @@
<!-- end row -->
<script>
document.addEventListener("DOMContentLoaded", function() {
const table = document.getElementById("tickets-table");
// Create custom dropdown
function createCustomDropdown(row) {
// Get the original dropdown items
const originalDropdown = row.querySelector('.dropdown-menu');
if (!originalDropdown) return null;
// Create new dropdown with proper background and spacing
const customDropdown = document.createElement('div');
customDropdown.className = 'custom-dropdown-menu';
// Copy inner content while maintaining icon alignment
customDropdown.innerHTML = originalDropdown.innerHTML;
return customDropdown;
}
let activeDropdown = null;
// Add click event listener to rows
table.querySelectorAll("tbody tr").forEach(row => {
const customDropdown = createCustomDropdown(row);
if (!customDropdown) return;
document.body.appendChild(customDropdown);
row.addEventListener("click", function(event) {
// Ignore clicks on the action column
if (event.target.closest('td:last-child')) {
return;
}
// Hide any active dropdown
if (activeDropdown) {
activeDropdown.style.display = 'none';
}
// Get click position
const rect = event.target.getBoundingClientRect();
// Position the dropdown with some offset
customDropdown.style.display = 'block';
customDropdown.style.position = 'fixed';
customDropdown.style.left = `${rect.left}px`;
customDropdown.style.top = `${rect.bottom + 5}px`; // Add 5px gap
// Set as active dropdown
activeDropdown = customDropdown;
event.stopPropagation();
});
// Preserve click handlers and add auto-close
customDropdown.querySelectorAll('.dropdown-item').forEach(item => {
item.addEventListener('click', function(e) {
event.preventDefault(e);
const onclickAttr = this.getAttribute('onclick');
if (onclickAttr) {
eval(onclickAttr);
}
const href = this.getAttribute('href');
if (href && href !== '#') {
window.location.href = href;
}
// Close the dropdown after handling the click
if (activeDropdown) {
activeDropdown.style.display = 'none';
activeDropdown = null;
}
e.stopPropagation();
});
});
});
// Close dropdown when clicking outside
document.addEventListener("click", function() {
if (activeDropdown) {
activeDropdown.style.display = 'none';
activeDropdown = null;
}
});
});
</script>
<script>
$(document).ready(function() {
$('#tickets-table').DataTable({
var table = $('#tickets-table').DataTable({
// dom: "<'row'<'col-12 d-flex justify-content-between align-items-center'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>" +
// "<'row'<'col-sm-12'tr>>" +
// "<'row'<'col-sm-5'i><'col-sm-7'p>>",
@ -227,72 +114,34 @@
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666; display:none;"></i>
</div>`,
searchPlaceholder: "Search",
emptyTable: '<div class="text-center text-muted">No Data found</div>'
emptyTable: '<div class="text-center text-muted">No Data found</div>',
paginate: {
previous: '◄',
next: '►'
}
},
paging: true,
});
})
const btnExport = document.querySelector("#btnExport");
const tableElement = document.querySelector("#tickets-table");
btnExport.addEventListener("click", () => {
const obj = new csvExport(tableElement);
const csvData = obj.exportCsv();
const blob = new Blob([csvData], {
type: "text/csv"
});
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "UserList.csv";
a.click();
setTimeout(() => {
URL.revokeObjectURL(url);
}, 500);
});
class csvExport {
constructor(table, header = true) {
this.table = table;
this.rows = Array.from(table.querySelectorAll("tr"));
if (!header && this.rows[0].querySelectorAll("th").length) {
this.rows.shift();
}
}
exportCsv() {
const lines = [];
const ncols = this._longestRow();
for (const row of this.rows) {
let line = "";
for (let i = 0; i < ncols - 1; i++) { // Exclude the last column
if (row.children[i] !== undefined) {
line += csvExport.safeData(row.children[i]);
}
line += i !== ncols - 2 ? "," : ""; // Adjusted for the last column exclusion
function applyBottomRowDropup() {
if (!table) return;
const currentRows = table.rows({ page: 'current' }).nodes().toArray();
$('#tickets-table tbody tr').removeClass('nh-force-dropup');
$('#tickets-table tbody tr td.table-action-cell .btn-group.dropdown').removeClass('dropup');
const targetCount = Math.min(2, currentRows.length);
for (let i = 0; i < targetCount; i++) {
const row = currentRows[currentRows.length - 1 - i];
if (!row) continue;
$(row).addClass('nh-force-dropup');
$(row).find('td.table-action-cell .btn-group.dropdown').addClass('dropup');
}
lines.push(line);
}
return lines.join("\n");
}
applyBottomRowDropup();
$('#tickets-table').on('draw.dt', applyBottomRowDropup);
_longestRow() {
return this.rows.reduce((length, row) => (row.childElementCount > length ? row.childElementCount : length), 0);
}
static safeData(td) {
let data = td.textContent;
//Replace all double quote to two double quotes
data = data.replace(/"/g, `""`);
//Replace , and \n to double quotes
data = /[",\n"]/.test(data) ? `"${data}"` : data;
return data;
}
}
})
function removeInsurer(element) {

View File

@ -1,10 +1,8 @@
<style>
/* ---- CSS cleaned/optimized ---- */
.table th, .table td { padding: 8px; }
table.dataTable tbody td { padding: 4px 4px !important; }
.col-12 { max-width: 98% !important; }
.dataTables_filter { position: absolute; }
.column-header { margin-right: 10px; }
.dataTables_length label {height: 21px !important;}
.filter-inline { display: flex; align-items: center; gap: 10px; }
.filter-inline input[type="date"] { padding: 8px 12px; border:1px solid #ddd; border-radius:4px; font-size:13px; background:white; cursor:pointer; }
.filter-inline input[type="date"]:focus { outline:none; border-color:#00a9a3; }
@ -205,19 +203,19 @@ table.dataTable tbody td { padding: 4px 4px !important; }
<div class="card-body" style="padding-top: unset !important; border: white !important; background: unset !important;">
<div>
<table data-custom-table-css="table" class="table table-striped mb-0 nowrap" cellspacing="0" id="policy_table">
<table data-custom-table-css="table" class="table mb-0 nowrap" cellspacing="0" id="policy_table">
<!-- <table data-custom-table-css="table" class="table table-borderless mb-0 nowrap" id="policy_table"> -->
<thead>
<thead class="bg-light">
<tr>
<th style="text-align:center;"><input type="checkbox" class="CB" id="selectAll" onchange="toggleSelectAll(this)"></th>
<th><div class="column-header">Policy No</div></th><!-- 2 -->
<th><div class="column-header">Customer</div></th> <!-- 3 -->
<th><div class="column-header">Premium</div></th> <!-- 4 -->
<th><div class="column-header">Policy Issues Date</div></th> <!-- 5 -->
<th><div class="column-header">Commission Amount</div></th> <!-- 6 -->
<th class="font-weight-medium" style="text-align:center;"><input type="checkbox" class="CB" id="selectAll" onchange="toggleSelectAll(this)"></th>
<th class="font-weight-medium">Policy No&nbsp;</th><!-- 2 -->
<th class="font-weight-medium">Customer&nbsp;</th> <!-- 3 -->
<th class="font-weight-medium">Premium&nbsp;</th> <!-- 4 -->
<th class="font-weight-medium">Policy Issues Date&nbsp;</th> <!-- 5 -->
<th class="font-weight-medium">Commission Amount&nbsp;</th> <!-- 6 -->
<?php if(isset($type) && $type === 'adjustment'): ?>
<th><div class="column-header">Paid Amount</div></th> <!-- 7 -->
<th class="font-weight-medium">Paid Amount&nbsp;</th> <!-- 7 -->
<?php endif; ?>
</tr>
</thead>
@ -424,7 +422,11 @@ table.dataTable tbody td { padding: 4px 4px !important; }
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666; display:none;"></i>
</div>`,
searchPlaceholder: "Search",
emptyTable: '<div class="text-center text-muted">No Data found</div>'
emptyTable: '<div class="text-center text-muted">No Data found</div>',
paginate: {
previous: '◄',
next: '►'
}
}
});
@ -947,7 +949,7 @@ function auditingHistory() {
let table = `
<div class="table-responsive">
<table data-custom-table-css="table"
class="table w-100 nowrap text-custom-black text-custom app-datatable">
class="table mb-0 nowrap" cellspacing="0">
<thead class="bg-light">
<!-- MAIN TITLE
@ -968,16 +970,16 @@ function auditingHistory() {
</tr>
<tr>
<th>S.No</th>
<th>Field Name</th>
<th>Old Value</th>
<th>New Value</th>
<th>Updated By</th>
<th>Date/Time</th>
<th class="font-weight-medium">S.No&nbsp;</th>
<th class="font-weight-medium">Field Name&nbsp;</th>
<th class="font-weight-medium">Old Value&nbsp;</th>
<th class="font-weight-medium">New Value&nbsp;</th>
<th class="font-weight-medium">Updated By&nbsp;</th>
<th class="font-weight-medium">Date/Time&nbsp;</th>
</tr>
</thead>
<tbody class="app-table-body">`;
<tbody>`;
// ============= MAIN INVOICE HISTORY =============
invoiceArray.forEach((row, index) => {

View File

@ -1,3 +1,19 @@
<style>
#table-kyc-docs_wrapper .datatable-search-wrapper{
width: auto !important;
}
#table-kyc-docs_wrapper .dataTables_filter input{
width: 220px !important;
padding-right: 34px !important;
}
#table-kyc-docs_wrapper .datatable-search-icon,
#table-kyc-docs_wrapper .datatable-clear-icon{
right: 11px !important;
}
</style>
<div class="tab-pane fade" id="branch-tab">
<div class="row float-right" style="padding-bottom: 10px;position: relative;right: 13px;">
@ -108,8 +124,9 @@
$(document).ready(function () {
var kyc_Docs_PrimaryKey = $('#kyc_General_PrimaryKey').val();
var kyc_Docs_PrimaryKey = $('#kyc_type_id').val();
var kyc_docs_form_action = '';
var kycDocsTable = null;
var myModal = new bootstrap.Modal(document.getElementById('kycdocsModal'));
myModal.hide();
// $('#add_branch').hide();
@ -118,7 +135,10 @@ $(document).ready(function () {
loadKYCDocsList();
$('#btnBranchAdd').click(function(){
$('#btnBranchAdd').click(function(e){
if (e && typeof e.preventDefault === 'function') {
e.preventDefault();
}
kyc_docs_form_action = '<?= base_url("master/kyc/kycdocs/createpost"); ?>';
var myModal = new bootstrap.Modal(document.getElementById('kycdocsModal'));
@ -149,7 +169,10 @@ $(document).ready(function () {
$('#designation2').val('');
})
$('.btnBack').click(function(){
$('.btnBack').click(function(e){
if (e && typeof e.preventDefault === 'function') {
e.preventDefault();
}
var myModal = new bootstrap.Modal(document.getElementById('kycdocsModal'));
myModal.hide();
// $('#add_branch').hide();
@ -183,7 +206,7 @@ $(document).ready(function () {
if(kyc_Docs_PrimaryKey !== ''){
var branchTable = '';
var data = <?= isset($insurer_branch) ? json_encode($insurer_branch) : '[]' ?>;
var data = <?= isset($kyc_docs) ? json_encode($kyc_docs) : '[]' ?>;
$.each(data, function(index, item) {
branchTable += `
<tr>
@ -195,7 +218,7 @@ $(document).ready(function () {
</tr>
`;
});
$('#insurer_branch_list').append(branchTable);
$('#kyc_docs_list').append(branchTable);
}
@ -356,7 +379,7 @@ $(document).ready(function () {
function loadKYCDocsList() {
var base_url = '<?= base_url("master/kyc/kycdocs/list/"); ?>';
var kyc_Docs_PrimaryKey_1 = $('#kyc_General_PrimaryKey').val();
var kyc_Docs_PrimaryKey_1 = $('#kyc_type_id').val();
// console.log("kyc_Docs_PrimaryKey_1");
// console.log(kyc_Docs_PrimaryKey_1);
@ -409,10 +432,50 @@ $(document).ready(function () {
setTimeout(() => {
$('#table-kyc-docs').DataTable({
paging: true ,
searching: true
kycDocsTable = $('#table-kyc-docs').DataTable({
paging: true,
searching: true,
autoWidth: false,
scrollX: false,
responsive: false,
columnDefs: [
{ targets: 0, width: '50%' },
{ targets: 1, width: '50%' }
],
initComplete: function () {
var $wrapper = $('#table-kyc-docs_wrapper');
$wrapper.find('.dataTables_scrollHeadInner').css({
'box-sizing': 'content-box',
'padding-right': '0px',
'width': '100%'
});
this.api().columns.adjust();
},
dom: "<'datatable-top-section'f>" +
"tr" +
"<'datatable-bottom-section d-flex justify-content-between align-items-center mt-2'<'datatable-info'i><'datatable-controls d-flex align-items-center'<'me-2'l>p>>",
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:11px; top:50%; transform:translateY(-53%); color:#666;"></i>
<i class="mdi mdi-close-circle datatable-clear-icon" style="position:absolute; right:11px; top:50%; transform:translateY(-53%); color:#666; display:none;"></i>
</div>`,
searchPlaceholder: "Search",
emptyTable: '<div class="text-center text-muted">No data available</div>'
}
});
// Table is inside a tab-pane; adjust once visible to keep th/td aligned.
$('a[data-toggle="tab"][href="#branch-tab"], a[data-bs-toggle="tab"][href="#branch-tab"]').on('shown.bs.tab', function () {
if (kycDocsTable) {
kycDocsTable.columns.adjust().draw(false);
}
});
if ($('#branch-tab').hasClass('active') || $('#branch-tab').hasClass('show')) {
kycDocsTable.columns.adjust().draw(false);
}
}, 2000);
});

View File

@ -3,17 +3,20 @@
<div class="col-12">
<div class="card-body subcard">
<div class="custom-form">
<form role="form" class="parsley-examples" method="post" id="kyc_general_form" enctype="multipart/form-data">
<form role="form" class="parsley-examples" method="post" id="kyc_general_form" enctype="multipart/form-data" data-parsley-validate>
<input type="hidden" value="<?= csrf_hash() ?>" name="<?= csrf_token() ?>" />
<input type="hidden" name="PrimaryKey" id="kyc_General_PrimaryKey" value="<?= isset($kyc['id']) ? $kyc['id'] : '' ?>"/>
<input type="hidden" name="kyc_type_id" id="kyc_type_id" value="<?= isset($kyc['id']) ? $kyc['id'] : '' ?>"/>
<div class="form-group">
<div class="form-row">
<div class="form-group col-md-4">
<label for="kyc_name">KYC Entity Type Name<span
class="text-danger">*</span></label>
<label for="kyc_name">KYC Entity Type Name<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="name"
placeholder="Enter Kyc Entity Type Name" value="<?= isset($kyc['name']) ? $kyc['name'] : '' ?>" name="name" required>
placeholder="Enter Kyc Entity Type Name" value="<?= isset($kyc['name']) ? $kyc['name'] : '' ?>" name="name"
required
data-parsley-pattern="^[a-zA-Z0-9\s/_-]+$"
data-parsley-pattern-message="This field may only contain letters, numbers, spaces, and the characters / - _"
data-parsley-trigger="keyup">
</div>
</div>
@ -119,35 +122,6 @@ $(document).ready(function () {
}
});
function validateForm() {
var isValid = true;
// Perform validation for each field
$('#kyc_general_form input, #kyc_general_form select').each(function() {
// Check for empty text inputs and selects
if ($(this).is('input[type="text"]') || $(this).is('select')) {
if ($.trim($(this).val()) == '') {
alert('Please fill in all fields');
isValid = false;
return false; // Exit the loop early
}
}
// Check for file inputs (assuming image files)
if ($(this).is('input[type="file"]')) {
var fileInput = $(this)[0];
if (fileInput.files.length === 0) {
alert('Please select an image file');
isValid = false;
return false; // Exit the loop early
}
}
});
return isValid;
}
});
</script>

View File

@ -49,7 +49,7 @@
<h4 style="position: relative;">KYC Entity Type</h4>
</div>
<div class="col-6" style="text-align: right;">
<a href="<?= base_url("master/kyc/list"); ?>" ><i class="mdi mdi-arrow-left" style="font-size: 17px;"></i> </a>
<a href="<?= base_url("master/kyc/list"); ?>" ><i class="ri-arrow-left-circle-line" style="font-size: 17px;"></i> </a>
</div>
</div> -->
<div class="tab-wrapper position-relative">

View File

@ -92,6 +92,94 @@
<!-- <script src="<?= base_url() . "public"; ?>/assets/libs/pdfmake/build/pdfmake.min.js"></script>
<script src="<?= base_url() . "public"; ?>/assets/libs/pdfmake/build/vfs_fonts.js"></script> -->
<!-- third party js ends -->
<script>
(function () {
if (typeof jQuery === 'undefined' || !jQuery.fn || !jQuery.fn.dataTable) {
return;
}
var $ = jQuery;
// Global defaults for all list pages unless overridden in a specific view.
// Use horizontal scroll (scrollX) instead of Responsive (+/- child rows).
$.extend(true, $.fn.dataTable.defaults, {
scrollX: true,
scrollY: '55vh',
scrollCollapse: true,
autoWidth: false,
pageLength: 10,
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
responsive: false,
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:11px; top:50%; transform:translateY(-53%); color:#666;"></i><i class="mdi mdi-close-circle datatable-clear-icon" style="position:absolute; right:11px; top:50%; transform:translateY(-53%); color:#666; display:none;"></i></div>',
searchPlaceholder: "Search",
emptyTable: '<div class="text-center text-muted">No Data found</div>'
}
});
// Per-table init can still pass responsive: true; force off so every list uses scrollX, not +/- rows.
$(document).on('preInit.dt', function (e, settings) {
if (settings && settings.oInit) {
settings.oInit.responsive = false;
}
});
function normalizeAddButtonLabel(buttonApi) {
if (!buttonApi || !buttonApi.length) {
return;
}
buttonApi.each(function () {
var node = this.node ? this.node() : null;
if (!node) {
return;
}
var $btn = $(node);
var rawText = ($btn.text() || '').trim().toLowerCase();
if (rawText !== 'add') {
return;
}
if ($btn.find('.mdi-plus').length) {
return;
}
$btn.html('<i class="mdi mdi-plus"></i><span class="btn-custom"> Add </span>');
});
}
function normalizeExportButtonLabel(buttonApi) {
if (!buttonApi || !buttonApi.length) {
return;
}
buttonApi.each(function () {
var node = this.node ? this.node() : null;
if (!node) {
return;
}
var $btn = $(node);
var rawText = ($btn.text() || '').trim().toLowerCase();
if (rawText !== 'export') {
return;
}
if ($btn.find('.mdi-menu-down').length) {
return;
}
$btn.html('<span class="btn-custom"> Export </span><i class="mdi mdi-menu-down"></i>');
});
}
// Keep Add CTA visual consistent across all DataTables pages.
$(document).on('init.dt', function (e, settings) {
if (!settings || !settings.nTable) {
return;
}
try {
var dt = $(settings.nTable).DataTable();
normalizeAddButtonLabel(dt.buttons());
normalizeExportButtonLabel(dt.buttons());
} catch (err) {
// Do not block table render if a specific page has non-standard buttons.
}
});
})();
</script>
<script src="<?= base_url() . "public"; ?>/assets/libs/bootstrap-datepicker/js/bootstrap-datepicker.min.js"></script>
<script src="<?= base_url() . "public"; ?>/assets/libs/moment/min/moment.min.js"></script>
@ -134,6 +222,26 @@
<!-- bootstrap datepicker -->
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.10.0/js/bootstrap-datepicker.min.js"></script> -->
<script>
/**
* Bootstrap popover compatibility shim for x-editable/bootstrap-editable.
* Some builds expect `$.fn.popover.defaults` (BS3 style) which is missing
* in newer Bootstrap plugin wrappers. Mirror Constructor defaults to keep
* old plugins working and avoid:
* "a.fn.popover.defaults is undefined".
*/
(function (window) {
if (!window.jQuery || !window.jQuery.fn || !window.jQuery.fn.popover) {
return;
}
var $ = window.jQuery;
var ctor = $.fn.popover.Constructor;
if (!$.fn.popover.defaults) {
$.fn.popover.defaults = (ctor && (ctor.Default || ctor.DEFAULT)) ? (ctor.Default || ctor.DEFAULT) : {};
}
})(window);
</script>
<script src="<?= base_url() . "public"; ?>/assets/js/pages/jquery.xeditable.js"></script>
<script src="<?= base_url() . "public"; ?>/assets/js/form-xeditable.init.js"></script>
<script src="<?= base_url() . "public"; ?>/assets/js/bootstrap-editable.min.js"></script>
@ -1287,6 +1395,379 @@ $(function(){
table.search('').draw();
});
/**
* NHance DataTables row action menus (tbody .dropdown)
*
* Moving the menu to body broke Bootstrap: clicks on Edit/Delete are no longer inside
* the .btn-group parent, so the menu closes before the link receives the click.
*
* Keep the menu in the DOM under the : data-display="static" (no Popper transforms),
* data-boundary="scrollParent" (no position-static parent). While open, set overflow
* visible on .table-responsive / scroll body so the menu is not clipped. If there is
* more room above the button than below, add Bootstraps .dropup so the menu opens
* upward (last rows / short viewport).
*/
(function () {
if (typeof jQuery === 'undefined') {
return;
}
var $ = jQuery;
var syncNs = '.nhDtRowToolbarDd';
function nhRowDropdownScrollHost($dd) {
var $body = $dd.closest('.dataTables_scrollBody');
if ($body.length) {
return $body;
}
return $dd.closest('.table-responsive');
}
function nhScheduleDropdownPopper($dd) {
var $toggle = $dd.children('[data-toggle="dropdown"]').first();
if (!$toggle.length) {
return;
}
var inst = $toggle.data('bs.dropdown');
if (inst && inst._popper) {
inst._popper.scheduleUpdate();
}
}
function nhRowActionDropdownOpts() {
$('.dataTables_wrapper table.dataTable tbody [data-toggle="dropdown"]').attr({
'data-boundary': 'scrollParent',
'data-display': 'static',
'data-flip': 'false'
});
}
function nhNormalizeActionCells(tableApi) {
if (!tableApi) {
return;
}
var $table = $(tableApi.table().node());
$table.find('tbody tr').each(function () {
var $row = $(this);
var $actionCell = $row.find('td').filter(function () {
return $(this).find('.btn-group.dropdown, .dropdown-menu').length > 0;
}).last();
if ($actionCell.length) {
$actionCell.addClass('table-action-cell');
}
});
}
function nhApplyBottomRowDropup(tableApi) {
if (!tableApi) {
return;
}
var $table = $(tableApi.table().node());
var rows = tableApi.rows({ page: 'current' }).nodes().toArray();
$table.find('tbody tr').removeClass('nh-force-dropup');
$table.find('tbody td.table-action-cell .btn-group.dropdown').removeClass('dropup');
var target = 0;
for (var i = rows.length - 1; i >= 0 && target < 2; i--) {
var $row = $(rows[i]);
var $group = $row.find('td.table-action-cell .btn-group.dropdown').first();
if (!$group.length) {
continue;
}
$row.addClass('nh-force-dropup');
$group.addClass('dropup');
target++;
}
}
$(function () {
var D = $.fn.dropdown && $.fn.dropdown.Constructor && $.fn.dropdown.Constructor.Default;
if (D) {
D.boundary = 'window';
}
nhRowActionDropdownOpts();
});
$(document).on('draw.dt', 'table.dataTable', function () {
nhRowActionDropdownOpts();
var dt = $(this).DataTable();
nhNormalizeActionCells(dt);
nhApplyBottomRowDropup(dt);
});
$(document).on('show.bs.dropdown', '.dataTables_wrapper table.dataTable tbody .dropdown', function () {
var $dd = $(this);
var $toggle = $dd.children('[data-toggle="dropdown"]').first();
var $menu = $dd.children('.dropdown-menu').first();
var el = $toggle[0];
var $row = $dd.closest('tr');
var isForcedDropup = $row.hasClass('nh-force-dropup');
if (!isForcedDropup) {
// Fallback: if row marker is missing, still force dropup on the last 2 visible rows.
var $tbodyRows = $row.closest('tbody').children('tr:visible');
if ($tbodyRows.length) {
var rowIdx = $tbodyRows.index($row);
isForcedDropup = rowIdx >= ($tbodyRows.length - 2);
}
}
var $scroll = $dd.closest('.dataTables_scrollBody');
if ($scroll.length) {
$scroll.data('nh-prev-overflow', $scroll.css('overflow'));
$scroll.css('overflow', 'visible');
}
var $resp = $dd.closest('.table-responsive');
if ($resp.length) {
$resp.data('nh-prev-overflow-tr', $resp.css('overflow'));
$resp.css('overflow', 'visible');
}
var $cardBody = $dd.closest('.card-body');
if ($cardBody.length) {
$cardBody.data('nh-prev-overflow-cb', $cardBody.css('overflow'));
$cardBody.css('overflow', 'visible');
}
if (!el || !el.getClientRects().length) {
return;
}
var rect = el.getBoundingClientRect();
var vh = window.innerHeight;
var below = Math.max(0, vh - rect.bottom - 8);
var above = Math.max(0, rect.top - 8);
var menuHeight = 0;
if ($menu.length) {
// Measure true menu height even while hidden.
var prevStyle = $menu.attr('style');
$menu.css({ display: 'block', visibility: 'hidden', maxHeight: '', overflowY: '' });
menuHeight = $menu.outerHeight() || 0;
if (prevStyle !== undefined) {
$menu.attr('style', prevStyle);
} else {
$menu.removeAttr('style');
}
}
// Pick direction by space; prefer the side that can fit the full menu.
var openUp = false;
if (isForcedDropup) {
openUp = true;
} else if (menuHeight > 0) {
if (below >= menuHeight) {
openUp = false;
} else if (above >= menuHeight) {
openUp = true;
} else {
openUp = above > below;
}
} else {
openUp = below < 150 && above > below;
}
// Keep explicit per-row dropup (set by nhApplyBottomRowDropup / page scripts).
if (!isForcedDropup) {
$dd.removeClass('dropup');
}
$dd.removeData('nh-auto-dropup');
if (openUp) {
$dd.addClass('dropup');
if (!isForcedDropup) {
$dd.data('nh-auto-dropup', 1);
}
}
// If there still isn't enough room, keep all actions reachable via scroll.
if ($menu.length && menuHeight > 0) {
var usableSpace = openUp ? above : below;
var maxVisible = Math.max(140, usableSpace - 12);
if (menuHeight > maxVisible) {
$menu.css({
maxHeight: maxVisible + 'px',
overflowY: 'auto'
});
} else {
$menu.css({
maxHeight: '',
overflowY: ''
});
}
}
});
$(document).on('hidden.bs.dropdown', '.dataTables_wrapper table.dataTable tbody .dropdown', function () {
var $dd = $(this);
var $scroll = $dd.closest('.dataTables_scrollBody');
if ($scroll.length) {
var o = $scroll.data('nh-prev-overflow');
if (o !== undefined) {
$scroll.css('overflow', o);
}
$scroll.removeData('nh-prev-overflow');
}
var $resp = $dd.closest('.table-responsive');
if ($resp.length) {
var o2 = $resp.data('nh-prev-overflow-tr');
if (o2 !== undefined) {
$resp.css('overflow', o2);
}
$resp.removeData('nh-prev-overflow-tr');
}
var $cardBody = $dd.closest('.card-body');
if ($cardBody.length) {
var o3 = $cardBody.data('nh-prev-overflow-cb');
if (o3 !== undefined) {
$cardBody.css('overflow', o3);
}
$cardBody.removeData('nh-prev-overflow-cb');
}
if ($dd.data('nh-auto-dropup')) {
$dd.removeClass('dropup');
$dd.removeData('nh-auto-dropup');
}
$dd.children('.dropdown-menu').css({
maxHeight: '',
overflowY: ''
});
});
$(document).on('shown.bs.dropdown', '.dataTables_wrapper .dropdown', function () {
var $dd = $(this);
if ($dd.closest('table.dataTable tbody').length) {
return;
}
var sync = function () {
nhScheduleDropdownPopper($dd);
};
nhScheduleDropdownPopper($dd);
nhRowDropdownScrollHost($dd).off(syncNs).on('scroll' + syncNs, sync);
$(window).off(syncNs).on('scroll' + syncNs, sync);
var $contentPage = $dd.closest('.content-page');
if ($contentPage.length) {
$contentPage.off(syncNs).on('scroll' + syncNs, sync);
}
});
$(document).on('hidden.bs.dropdown', '.dataTables_wrapper .dropdown', function () {
var $dd = $(this);
if ($dd.closest('table.dataTable tbody').length) {
return;
}
nhRowDropdownScrollHost($dd).off(syncNs);
$(window).off(syncNs);
$dd.closest('.content-page').off(syncNs);
});
/**
* NHance standardize DataTables control layout for pages still using
* default DataTables DOM:
* top: length + search, bottom: info + paginate
*
* Requirement format:
* - top row: search (left), buttons (right if present)
* - bottom row: info (left), show-length + pagination (right)
*
* For wrappers without custom dom, move top `.dataTables_length` to
* bottom row before `.dataTables_paginate`.
*/
function normalizeDataTableLayout(wrapper) {
if (!wrapper || !wrapper.length) {
return;
}
var $rows = wrapper.children('div.row');
if ($rows.length < 2) {
return;
}
var $top = $rows.first();
var $bottom = $rows.last();
var $topLength = $top.find('div.dataTables_length').first();
var $bottomPaginate = $bottom.find('div.dataTables_paginate').first();
if (!$topLength.length || !$bottomPaginate.length) {
applyBottomSlotClasses($bottom);
return;
}
if ($bottom.find('div.dataTables_length').length) {
applyBottomSlotClasses($bottom);
return; // already normalized/customized
}
$bottomPaginate.before($topLength);
applyBottomSlotClasses($bottom);
}
/**
* Mark bottom row containers so CSS can enforce exact requirement layout:
* info left; length + pagination grouped right.
*/
function applyBottomSlotClasses($bottomRow) {
if (!$bottomRow || !$bottomRow.length) {
return;
}
$bottomRow.children('div').removeClass('nh-dt-bottom-info nh-dt-bottom-length nh-dt-bottom-paginate');
var $info = $bottomRow.find('div.dataTables_info').first().closest('div');
var $len = $bottomRow.find('div.dataTables_length').first().closest('div');
var $pag = $bottomRow.find('div.dataTables_paginate').first().closest('div');
if ($info.length) {
$info.addClass('nh-dt-bottom-info');
}
if ($len.length) {
$len.addClass('nh-dt-bottom-length');
}
if ($pag.length) {
$pag.addClass('nh-dt-bottom-paginate');
}
}
$(document).on('init.dt', function (e, settings) {
if (!settings || !settings.nTableWrapper) {
return;
}
var wrapper = $(settings.nTableWrapper);
normalizeDataTableLayout(wrapper);
/* Stops Bootstrap `.table-responsive` from adding a second horizontal scrollbar under pagination */
wrapper.closest('.table-responsive').addClass('nh-dt-no-outer-scroll');
try {
var dt = $(settings.nTable).DataTable();
nhNormalizeActionCells(dt);
nhApplyBottomRowDropup(dt);
} catch (err) {
// keep page running if a table has non-standard setup
}
});
/**
* Prev/next disabled state (first page, last page, single page): DataTables
* normally sets `li.page-item.disabled`; re-sync from page.info() after each
* draw so grey-arrow styles in custom.css / header always match behaviour.
*/
$(document).on('draw.dt', function (e, settings) {
if (!settings || !settings.nTableWrapper || !settings.oFeatures || !settings.oFeatures.bPaginate) {
return;
}
var api;
try {
api = new $.fn.dataTable.Api(settings);
} catch (err) {
return;
}
var info = api.page.info();
var $pag = $(settings.nTableWrapper).find('.dataTables_paginate');
if (!$pag.length) {
return;
}
var pages = info.pages;
var atFirst = info.page <= 0;
var atLast = pages <= 0 || info.page >= pages - 1;
$pag.find('li.paginate_button.page-item.previous, li.page-item.previous').toggleClass('disabled', atFirst);
$pag.find('li.paginate_button.page-item.next, li.page-item.next').toggleClass('disabled', atLast);
$pag.find('a.paginate_button.previous').toggleClass('disabled', atFirst);
$pag.find('a.paginate_button.next').toggleClass('disabled', atLast);
});
})();
</script>

View File

@ -30,6 +30,7 @@
<link href="<?= base_url() . "public"; ?>/assets/css/bootstrap-creative-dark.min.css" rel="stylesheet" type="text/css" id="bs-dark-stylesheet" />
<link href="<?= base_url() . "public"; ?>/assets/css/app-creative-dark.min.css" rel="stylesheet" type="text/css" id="app-dark-stylesheet" />
<link href="<?= base_url() . "public"; ?>/assets/css/custom.css" rel="stylesheet" type="text/css" />
<!-- icons -->
<link href="<?= base_url() . "public"; ?>/assets/css/icons.min.css" rel="stylesheet" type="text/css" />
@ -828,43 +829,164 @@
border: none !important;
}
/* Previous / Next buttons */
.dataTables_wrapper .dataTables_paginate .page-item.previous .page-link,
.dataTables_wrapper .dataTables_paginate .page-item.next .page-link {
.dataTables_wrapper .dataTables_paginate .page-item.previous,
.dataTables_wrapper .dataTables_paginate .page-item.next {
display: inline-flex !important;
align-items: center !important;
align-self: center !important;
}
/* Previous / Next — white background; box matches page number height; arrow via ::before */
.dataTables_wrapper .dataTables_paginate .page-item.previous:not(.disabled) .page-link,
.dataTables_wrapper .dataTables_paginate .page-item.next:not(.disabled) .page-link {
border-radius: 13px !important;
background-color: #F5FFFF;
background-color: #ffffff !important;
border: none !important;
box-shadow: 0 0 0 0.15rem #F5FFFF;
font-size: 0;
/* hide default text */
position: relative;
box-shadow: none !important;
outline: none !important;
font-size: 0 !important;
line-height: 0 !important;
color: transparent !important;
position: relative !important;
overflow: hidden !important;
min-width: 32px !important;
width: 32px !important;
min-height: 2.25rem !important;
padding: 0 !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
}
/* Custom arrows */
.dataTables_wrapper .dataTables_paginate .page-item.previous .page-link::before {
content: "";
font-size: 14px;
/* Previous / Next — disabled (first page, last page, or single page): white btn, grey arrows */
.dataTables_wrapper .dataTables_paginate .page-item.previous.disabled .page-link,
.dataTables_wrapper .dataTables_paginate .page-item.next.disabled .page-link {
border-radius: 13px !important;
background-color: #ffffff !important;
border: none !important;
box-shadow: none !important;
outline: none !important;
font-size: 0 !important;
line-height: 0 !important;
color: transparent !important;
position: relative !important;
overflow: hidden !important;
min-width: 32px !important;
width: 32px !important;
min-height: 2.25rem !important;
padding: 0 !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
cursor: not-allowed !important;
pointer-events: none !important;
opacity: 1 !important;
}
.dataTables_wrapper .dataTables_paginate .page-item.previous.disabled .page-link::before,
.dataTables_wrapper .dataTables_paginate .page-item.next.disabled .page-link::before {
color: #adb5bd !important;
}
/* DataTables variant without .page-item/.page-link markup */
.dataTables_wrapper .dataTables_paginate .paginate_button.previous:not(.disabled),
.dataTables_wrapper .dataTables_paginate .paginate_button.next:not(.disabled) {
border-radius: 13px !important;
background-color: #ffffff !important;
border: none !important;
box-shadow: none !important;
outline: none !important;
font-size: 0 !important;
line-height: 0 !important;
color: transparent !important;
position: relative !important;
overflow: hidden !important;
min-width: 32px !important;
width: 32px !important;
min-height: 2.25rem !important;
padding: 0 !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
}
.dataTables_wrapper .dataTables_paginate .paginate_button.previous.disabled,
.dataTables_wrapper .dataTables_paginate .paginate_button.next.disabled {
border-radius: 13px !important;
background-color: #ffffff !important;
border: none !important;
box-shadow: none !important;
outline: none !important;
font-size: 0 !important;
line-height: 0 !important;
min-width: 32px !important;
width: 32px !important;
min-height: 2.25rem !important;
padding: 0 !important;
position: relative !important;
overflow: hidden !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
cursor: not-allowed !important;
pointer-events: none !important;
opacity: 1 !important;
}
.dataTables_wrapper .dataTables_paginate .paginate_button.previous.disabled::before,
.dataTables_wrapper .dataTables_paginate .paginate_button.next.disabled::before {
color: #adb5bd !important;
}
/* Arrows: dead-center in the hit box (Unicode glyphs have uneven metrics otherwise) */
.dataTables_wrapper .dataTables_paginate .page-item.previous .page-link::before,
.dataTables_wrapper .dataTables_paginate .page-item.next .page-link::before {
content: "";
font-size: 14px;
content: "";
position: absolute !important;
left: 50% !important;
top: 50% !important;
transform: translate(-50%, -50%) !important;
line-height: 1 !important;
font-size: 12px !important;
display: block !important;
margin: 0 !important;
}
.dataTables_wrapper .dataTables_paginate .page-item.previous:not(.disabled) .page-link::before {
content: "";
color: #333333;
}
.dataTables_wrapper .dataTables_paginate .page-item.next:not(.disabled) .page-link::before {
content: "";
color: #333333;
}
.dataTables_wrapper .dataTables_paginate .page-item.previous.disabled .page-link::before {
content: "";
}
/* Active / Inactive pages */
.dataTables_wrapper .dataTables_paginate .page-item.next.disabled .page-link::before {
content: "";
}
/* Avoid double arrows when Bootstrap markup nests .paginate_button + .page-link */
.dataTables_wrapper .dataTables_paginate li.paginate_button.previous::before,
.dataTables_wrapper .dataTables_paginate li.paginate_button.next::before {
content: none !important;
}
/* Active / Inactive page numbers only (not prev/next arrows) */
.dataTables_wrapper .dataTables_paginate .page-item.active .page-link {
color: #fff !important;
border-radius: 13px !important;
}
.dataTables_wrapper .dataTables_paginate .page-item:not(.active) .page-link {
.dataTables_wrapper .dataTables_paginate .page-item:not(.active):not(.previous):not(.next) .page-link {
border: 1px solid #fff !important;
border-radius: 13px !important;
}
/* Info text */
.dataTables_info {
font-size: 12px;
@ -959,8 +1081,7 @@
.card-body .card-body .dt-buttons,
.card-body .card-body .datatable-buttons {
margin-right: 0 !important;
padding-right: 2.25rem !important;
/* match card-body padding */
padding-right: 0 !important;
}
/* Applies only to tables with data-custom-table-css="table" */
@ -1424,7 +1545,7 @@
}
.dropdown {
.topbar-actions .dropdown {
margin-right: 10px !important;
}
@ -2389,6 +2510,20 @@ body[data-sidebar-size="condensed"] .footer {
$default = base_url('public/assets/images/avatar_2x.png');
$firstName = isset(get_session_userdata()->first_name) ? get_session_userdata()->first_name : 'A';
$initial = strtoupper(substr($firstName, 0, 1));
$roleId = get_role_id();
$roleName = 'User';
if ($roleId == ADMIN_ROLE_ID) {
$roleName = 'Admin';
} elseif ($roleId == HEAD_ROLE_ID) {
$roleName = 'Head';
} elseif ($roleId == MANAGER_ROLE_ID) {
$roleName = 'Manager';
} elseif ($roleId == ACCOUNT_MANAGER_ROLE_ID) {
$roleName = 'Account Manager';
} elseif ($roleId == STAFF_ROLE_ID) {
$roleName = 'Staff';
}
$userTitle = $firstName . ' ( ' . $roleName . ' )';
?>
<?php if (!empty($profile)) : ?>
@ -2397,18 +2532,19 @@ body[data-sidebar-size="condensed"] .footer {
src="<?= $profile ?>"
onerror="this.onerror=null; this.src='<?= $default ?>'; this.style.display='none'; document.getElementById('avatar-fallback').style.display='flex';"
alt="user-image"
title="<?= $userTitle ?>"
height="38" width="38"
class="rounded-circle"
style="object-fit:cover; border: 2px solid var(--primary-light); cursor:pointer;"
>
<!-- Fallback avatar (hidden by default, shown if image fails) -->
<div class="avatar-btn" id="avatar-fallback" title="<?= $firstName ?>" style="display:none;">
<div class="avatar-btn" id="avatar-fallback" title="<?= $userTitle ?>" style="display:none;">
<?= $initial ?>
</div>
<?php else : ?>
<!-- No profile image show initial avatar -->
<div class="avatar-btn" title="<?= $firstName ?>">
<div class="avatar-btn" title="<?= $userTitle ?>">
<?= $initial ?>
</div>
<?php endif; ?>

View File

@ -1,19 +1,6 @@
<style>
.table th,
.table td {
padding: 8px;
}
table.dataTable tbody td {
padding: 4px 4px !important;
}
.col-12 {
max-width: 100% !important;
}
.dataTables_filter {
position: absolute;
max-width: 98% !important;
}
.highlight {
@ -49,32 +36,6 @@ table.dataTable tbody td {
}
.custom-dropdown-menu {
display: none;
position: absolute;
background-color: #ffffff !important;
border: 1px solid rgba(0, 0, 0, 0.15);
border-radius: 0.25rem;
padding: 0.5rem 0;
min-width: 10rem;
z-index: 9999;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.custom-dropdown-menu .dropdown-item {
display: block !important;
width: 100% !important;
padding: 0.5rem 1rem !important;
color: #212529 !important;
text-decoration: none !important;
background-color: transparent !important;
}
.custom-dropdown-menu .dropdown-item:hover {
background-color: #f8f9fa !important;
color: #16181b !important;
cursor: pointer !important;
}
.addbtnStyle{
margin-left: 20px !important;
@ -173,23 +134,23 @@ table.dataTable tbody td {
</div>
<div>
<div class="table-responsive">
<table data-custom-table-css="table" class="table mb-0 nowrap w-100 table-centered" cellspacing="0" id="tickets-table">
<table data-custom-table-css="table" class="table mb-0 nowrap" cellspacing="0" id="tickets-table">
<thead class="bg-light">
<tr>
<th><div class="column-header">S.No &nbsp;</div></th>
<th><div class="column-header">Opportunity Type &nbsp;</div></th>
<th><div class="column-header">Issuer &nbsp;</div></th>
<th><div class="column-header">Client Type &nbsp;</div></th>
<th><div class="column-header">Client / Branch &nbsp;</div></th>
<th><div class="column-header">Entity Type &nbsp;</div></th>
<th><div class="column-header">Branch Name &nbsp;</div></th>
<th><div class="column-header">Branch Code &nbsp;</div></th>
<th><div class="column-header">Contact Person Name &nbsp;</div></th>
<th><div class="column-header">Contact Person Mobile &nbsp;</div></th>
<th><div class="column-header">Policy Type &nbsp;</div></th>
<th><div class="column-header">Status &nbsp;</div></th>
<th><div class="column-header">Action &nbsp;</div></th>
<th class="font-weight-medium">S.No&nbsp;</th>
<th class="font-weight-medium">Opportunity Type&nbsp;</th>
<th class="font-weight-medium">Issuer&nbsp;</th>
<th class="font-weight-medium">Client Type&nbsp;</th>
<th class="font-weight-medium">Client / Branch&nbsp;</th>
<th class="font-weight-medium">Entity Type&nbsp;</th>
<th class="font-weight-medium">Branch Name&nbsp;</th>
<th class="font-weight-medium">Branch Code&nbsp;</th>
<th class="font-weight-medium">Contact Person Name&nbsp;</th>
<th class="font-weight-medium">Contact Person Mobile&nbsp;</th>
<th class="font-weight-medium">Policy Type&nbsp;</th>
<th class="font-weight-medium">Status&nbsp;</th>
<th class="font-weight-medium text-center">Action&nbsp;</th>
</tr>
</thead>
@ -220,12 +181,11 @@ table.dataTable tbody td {
<td><?php echo $row['contact_person_mobile'] ?? '-'; ?></td>
<td><?php echo $row['policy_type'] ?? '-'; ?></td>
<td><?php echo $lead_status[$row['status']] ?? '-'; ?></td>
<td>
<td class="text-center table-action-cell">
<div class="btn-group dropdown">
<a href="javascript:void(0);" class="dropdown-toggle arrow-none btn btn-light btn-sm"
data-toggle="dropdown"
aria-expanded="false"
data-boundary="window"> <i class="mdi mdi-dots-horizontal"></i>
aria-expanded="false"> <i class="mdi mdi-dots-horizontal"></i>
</a>
<div class="dropdown-menu dropdown-menu-right">
<?php $isNonEb = isset($row['lead_form_type']) && (int) $row['lead_form_type'] === 2; ?>
@ -751,186 +711,7 @@ table.dataTable tbody td {
};
// New menu
document.addEventListener("DOMContentLoaded", function () {
const table = document.getElementById("tickets-table");
// RFQ_or_QCR: 1 = RFQ (insurer/internal), 2 = QCR (client)
window.RFQ_or_QCR = 1;
// Logged-in user role id (used in mail modal rendering)
window.user_role_id = <?php echo get_role_id(); ?>;
function createCustomDropdown(row) {
const originalDropdown = row.querySelector('.dropdown-menu');
if (!originalDropdown) return null;
const customDropdown = document.createElement('div');
customDropdown.className = 'custom-dropdown-menu';
// Clone the items but remove their original click handlers
const items = originalDropdown.querySelectorAll('.dropdown-item');
items.forEach(item => {
const newItem = item.cloneNode(true);
// Preserve the attributes but remove the onclick
newItem.removeAttribute('onclick');
customDropdown.appendChild(newItem);
});
return customDropdown;
}
let activeDropdown = null;
table.querySelectorAll("tbody tr").forEach(row => {
const customDropdown = createCustomDropdown(row);
if (!customDropdown) return;
document.body.appendChild(customDropdown);
row.addEventListener("click", function(event) {
if (event.target.closest('.dropdown-toggle')) {
event.stopPropagation();
const originalDropdown = event.target.closest('.btn-group').querySelector('.dropdown-menu');
if (originalDropdown) {
if (activeDropdown) {
activeDropdown.style.display = 'none';
}
const rect = event.target.getBoundingClientRect();
customDropdown.style.display = 'block';
customDropdown.style.position = 'fixed';
customDropdown.style.left = `${rect.left}px`;
customDropdown.style.top = `${rect.bottom + 5}px`;
activeDropdown = customDropdown;
}
return;
}
if (event.target.closest('td:last-child')) {
return;
}
if (activeDropdown) {
activeDropdown.style.display = 'none';
}
const rect = event.target.getBoundingClientRect();
customDropdown.style.display = 'block';
customDropdown.style.position = 'fixed';
customDropdown.style.left = `${rect.left}px`;
customDropdown.style.top = `${rect.bottom + 5}px`;
activeDropdown = customDropdown;
event.stopPropagation();
});
customDropdown.querySelectorAll('.dropdown-item').forEach(item => {
item.addEventListener('click', function(e) {
console.log("##############");
e.preventDefault();
e.stopPropagation();
// Get the data-id and other attributes from the original button
const originalItem = row.querySelector(`.dropdown-item[data-id="${this.getAttribute('data-id')}"]`);
// For edit functionality
if (this.classList.contains('btnEdit')) {
const id = this.getAttribute('data-id');
const lead_form_type = this.getAttribute('data-ft');
console.log('second drop lead_form_type', lead_form_type)
if (id) {
getLeadsDataForEdit(id, lead_form_type);
}
}else if (this.classList.contains('btnHistory')) {
let lead_id = this.getAttribute('data-id');
if(lead_id){ console.log("******** Clicked from td lead_id-", lead_id); getLeadsDataForMailHistory(lead_id); }
else{ console.log("******** Clicked from td but i don`t have lead_id"); }
}else if (this.classList.contains('btnRfqSheetList')) {
const leadId = this.getAttribute('data-id');
if (leadId) {
createRfqSheetFromList(leadId);
}
}else if (this.classList.contains('btnQcrSheetList')) {
const leadId = this.getAttribute('data-id');
if (leadId) {
createQcrSheetFromList(leadId);
}
}else if (this.classList.contains('btnInternalMailList')) {
const leadId = this.getAttribute('data-id');
if (leadId) {
openInternalMailFromList(leadId);
}
}else if (this.classList.contains('btnInsurerMailList')) {
const leadId = this.getAttribute('data-id');
if (leadId) {
openInsurerMailFromList(leadId);
}
}else if (this.classList.contains('btnClientMailList')) {
const leadId = this.getAttribute('data-id');
if (leadId) {
openClientMailFromList(leadId);
}
}else if (this.classList.contains('btnPlacementList')) {
const leadId = this.getAttribute('data-id');
if (leadId) {
openPlacementFromList(leadId);
}
}
else{
const onclickAttr = this.getAttribute('onclick');
if (onclickAttr) {
eval(onclickAttr);
}
}
// For RFQ links
const href = this.getAttribute('href');
if (
href &&
href !== '#' &&
!this.classList.contains('btnEdit') &&
!this.classList.contains('btnRfqSheetList') &&
!this.classList.contains('btnQcrSheetList') &&
!this.classList.contains('btnInternalMailList') &&
!this.classList.contains('btnInsurerMailList') &&
!this.classList.contains('btnClientMailList') &&
!this.classList.contains('btnPlacementList')
) {
window.location.href = href;
}
// Close the dropdown
if (activeDropdown) {
activeDropdown.style.display = 'none';
activeDropdown = null;
}
});
});
});
// Close dropdown when clicking outside
document.addEventListener("click", function() {
if (activeDropdown) {
activeDropdown.style.display = 'none';
activeDropdown = null;
}
});
document.addEventListener("click", function(e) {
const historyBtn = e.target.closest(".btnHistory");
if (historyBtn && historyBtn.dataset.id) {
let lead_id = historyBtn.dataset.id;
console.log("******** in triple dot id available - " + lead_id);
getLeadsDataForMailHistory(lead_id);
}
});
});
var table;
// Datatable document ready
$(document).ready(function() {
@ -938,8 +719,7 @@ table.dataTable tbody td {
var ticketsTable = $('#tickets-table');
if (ticketsTable.length) {
ticketsTable.DataTable({
scrollX: true,
table = ticketsTable.DataTable({
// dom: "<'row'<'col-sm-7'f><'col-sm-5 text-right'B>>" + // Filter left, buttons right
// "<'row'<'col-sm-12'tr>>" +
// "<'row'<'col-sm-5'i><'col-sm-7'p>>", // Pagination at the bottom, without page length selector
@ -989,12 +769,34 @@ table.dataTable tbody td {
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666; display:none;"></i>
</div>`,
searchPlaceholder: "Search",
emptyTable: '<div class="text-center text-muted">No Data found</div>'
emptyTable: '<div class="text-center text-muted">No Data found</div>',
paginate: {
previous: '◄',
next: '►'
}
},
paging: true, // Enable pagination
pageLength: 10, // Set default number of rows per page (optional)
// ordering: false,
});
function applyBottomRowDropup() {
if (!table) return;
const currentRows = table.rows({ page: 'current' }).nodes().toArray();
$('#tickets-table tbody tr').removeClass('nh-force-dropup');
$('#tickets-table tbody tr td.table-action-cell .btn-group.dropdown').removeClass('dropup');
const targetCount = Math.min(2, currentRows.length);
for (let i = 0; i < targetCount; i++) {
const row = currentRows[currentRows.length - 1 - i];
if (!row) continue;
$(row).addClass('nh-force-dropup');
$(row).find('td.table-action-cell .btn-group.dropdown').addClass('dropup');
}
}
applyBottomRowDropup();
ticketsTable.on('draw.dt', applyBottomRowDropup);
} else {
console.error("Table not found.");
}
@ -1050,18 +852,18 @@ table.dataTable tbody td {
console.log("******** 1" + response.status);
let table = `
<div class="table-responsive">
<table data-custom-table-css="table" id="historytable" class="table table-striped mb-0 nowrap" cellspacing="0" id="tickets-table">
<table data-custom-table-css="table" id="historytable" class="table mb-0 nowrap" cellspacing="0">
<thead class="bg-light">
<tr>
<th><div class="column-header">S.No&nbsp;</div></th>
<th><div class="column-header">From Mail&nbsp;</div></th>
<th><div class="column-header">To Mail&nbsp;</div></th>
<th><div class="column-header">BCC&nbsp;</div></th>
<th><div class="column-header">CC&nbsp;</div></th>
<th><div class="column-header">Type&nbsp;</div></th>
<th><div class="column-header">Status&nbsp;</div></th>
<th><div class="column-header">Date/Time&nbsp;</div></th>
<th class="font-weight-medium">S.No&nbsp;</th>
<th class="font-weight-medium">From Mail&nbsp;</th>
<th class="font-weight-medium">To Mail&nbsp;</th>
<th class="font-weight-medium">BCC&nbsp;</th>
<th class="font-weight-medium">CC&nbsp;</th>
<th class="font-weight-medium">Type&nbsp;</th>
<th class="font-weight-medium">Status&nbsp;</th>
<th class="font-weight-medium">Date/Time&nbsp;</th>
</tr>
</thead>
<tbody>
@ -2350,4 +2152,5 @@ table.dataTable tbody td {
console.log('Developer Notice: The file lead_filter.php must be loaded by the controller for the filters to work, not leads_list.php.');
}
});
</script>
</script>
</script>

View File

@ -70,18 +70,22 @@
</div>
<div class="modal-body p-4">
<!-- <form class="parsley-examples" id="nhanceBranchForm" enctype="multipart/form-data"> -->
<form id="nhanceBranchForm" enctype="multipart/form-data">
<form id="nhanceBranchForm" enctype="multipart/form-data" data-parsley-validate>
<input type="hidden" name="pk" id="nhance_branch_id"/>
<div class="form-group">
<div class="form-row">
<div class="form-group col-md-12">
<label for="branch_name">Branch Name<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="branch_name" name="branch_name" placeholder="Enter Branch Name" required>
<input type="text" class="form-control" id="branch_name" name="branch_name" placeholder="Enter Branch Name"
required
data-parsley-pattern="^[a-zA-Z0-9\s_-]+$"
data-parsley-pattern-message="Branch name may only contain alphanumeric, space, underscore, and dash characters."
data-parsley-trigger="keyup">
</div>
</div>
</div>
<div class="form-group text-right m-b-0">
<button type="button" class="btn app-btn-secondary waves-effect waves-light" id="btnSubmit" onclick="handleSaveEditAndDelete('submit')">Submit</button>
<button type="submit" class="btn app-btn-secondary waves-effect waves-light" id="btnSubmit">Submit</button>
</div>
</form>
</div>
@ -104,7 +108,7 @@
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
buttons: [
{
text: 'Add',
text: '<i class="mdi mdi-plus" ></i><span class=" btn-custom"> Add </span>',
className: 'btn app-btn-primary mr-2',
action: function(e, dt, node, config) {
resetValues();openModal();
@ -146,7 +150,10 @@
});
$('.close').click(function(){
resetValues()
resetValues();
if ($('#nhanceBranchForm').parsley()) {
$('#nhanceBranchForm').parsley().reset();
}
})
function openModal(){
@ -324,6 +331,9 @@
function resetValues(){
$('#modalLabel').text('Add Branch');
$('#nhanceBranchForm')[0].reset();
if ($('#nhanceBranchForm').parsley()) {
$('#nhanceBranchForm').parsley().reset();
}
}
function appendEditData(data){
@ -331,5 +341,13 @@
$('#branch_name').val(data[0]['branch_name']);
openModal()
}
</script>
$('#nhanceBranchForm').on('submit', function(e) {
e.preventDefault();
var $form = $(this);
$form.parsley().validate();
if ($form.parsley().isValid()) {
handleSaveEditAndDelete('submit');
}
});
</script>

View File

@ -211,7 +211,7 @@
<th class="font-weight-medium">Status</th>
<!-- <th class="font-weight-medium">Agent</th> -->
<th class="font-weight-medium">POS</th>
<th class="font-weight-medium">Action</th>
<th class="font-weight-medium text-center">Action&nbsp;</th>
</tr>
</thead>
<tbody>
@ -227,7 +227,7 @@
<td> <?= $row['status_text'] ?> </td>
<!-- <td> <?= $row['agent_name'] ?? " - " ?> </td> -->
<td> <?= $row['pos_name'] ?? " - " ?> </td>
<td>
<td class="text-center table-action-cell">
<div class="btn-group dropdown">
<a href="javascript: void(0);" class="dropdown-toggle arrow-none btn btn-light btn-sm" data-toggle="dropdown" aria-expanded="false"><i class="mdi mdi-dots-horizontal"></i></a>
<div class="dropdown-menu dropdown-menu-right">
@ -304,7 +304,6 @@
if (tableEl.length) {
var ticketsTable = tableEl.DataTable({
scrollX: true,
// dom: "<'row'<'col-sm-6'f><'col-sm-6 text-right'B>>" + // Filter left, buttons right
// "<'row'<'col-sm-12'tr>>" +
// "<'row'<'col-sm-5'i><'col-sm-7'p>>", // Pagination at the bottom, without page length selector
@ -320,13 +319,13 @@
openPayoutFilterNav();
}
},
// {
// text: 'Add',
// className: 'btn app-btn-primary mr-2',
// action: function (e, dt, node, config) {
// window.location.href = "<?= base_url('payout/invoices?type=add') ?>";
// }
// },
{
text: '<i class="mdi mdi-plus" ></i><span class=" btn-custom"> Add </span>',
className: 'btn app-btn-primary mr-2',
action: function (e, dt, node, config) {
window.location.href = "<?= base_url('payout/invoices?type=add') ?>";
}
},
{
extend: 'collection',
text: '<span class=" btn-custom"> Export </span><i class="mdi mdi-menu-down"></i>',
@ -363,12 +362,33 @@
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666; display:none;"></i>
</div>`,
searchPlaceholder: "Search",
emptyTable: '<div class="text-center text-muted">No Data found</div>'
emptyTable: '<div class="text-center text-muted">No Data found</div>',
paginate: {
previous: '◄',
next: '►'
}
},
paging: true, // Enable pagination
pageLength: 10, // Set default number of rows per page (optional)
ordering: false
});
function applyBottomRowDropup() {
if (!ticketsTable) return;
const currentRows = ticketsTable.rows({ page: 'current' }).nodes().toArray();
$('#scroll-horizontal-datatable tbody tr').removeClass('nh-force-dropup');
$('#scroll-horizontal-datatable tbody tr td.table-action-cell .btn-group.dropdown').removeClass('dropup');
const targetCount = Math.min(2, currentRows.length);
for (let i = 0; i < targetCount; i++) {
const row = currentRows[currentRows.length - 1 - i];
if (!row) continue;
$(row).addClass('nh-force-dropup');
$(row).find('td.table-action-cell .btn-group.dropdown').addClass('dropup');
}
}
applyBottomRowDropup();
ticketsTable.on('draw.dt', applyBottomRowDropup);
// IMPORTANT — DataTables draw event REF: TTS
ticketsTable.on('draw.dt', function () {

View File

@ -1,15 +1,6 @@
<style>
.table th,
.table td {
padding: 8px;
}
table.dataTable tbody td {
padding: 4px 4px !important;
}
.dataTables_filter {
position: absolute;
.col-12 {
max-width: 98% !important;
}
.column-header {
@ -28,38 +19,6 @@
margin-bottom: 10px;
}
.custom-dropdown-menu {
display: none;
position: fixed;
background-color: #ffffff !important;
border: 1px solid rgba(0, 0, 0, .15);
border-radius: 0.25rem;
padding: 0.5rem 0;
min-width: 10rem;
z-index: 9999;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.custom-dropdown-menu .dropdown-item {
display: block !important;
width: 100% !important;
padding: 0.5rem 1rem !important;
clear: both !important;
font-weight: 400 !important;
color: #212529 !important;
text-align: inherit !important;
white-space: nowrap !important;
background-color: transparent !important;
border: 0 !important;
text-decoration: none !important;
position: relative !important;
}
.custom-dropdown-menu .dropdown-item:hover {
background-color: #f8f9fa !important;
color: #16181b !important;
cursor: pointer !important;
}
.custom-dropdown-menu .dropdown-item i {
margin-right: 8px !important;
@ -315,23 +274,23 @@
<div class="col-12" id="endorsement_list">
<div class="card">
<div class="card-body">
<table data-custom-table-css="table" class="table table-striped mb-0 nowrap" cellspacing="0" id="tickets-table">
<table data-custom-table-css="table" class="table mb-0 nowrap" cellspacing="0" id="tickets-table">
<thead class="bg-light">
<tr>
<th>Sno</th>
<th>Issuer</th>
<th>Client</th>
<th>Branch</th>
<th>Insurer</th>
<th>Policy</th>
<th>Endorsement Type</th>
<th>Endorsement No</th>
<th>Data Recived Date</th>
<th>No of Insured</th>
<th>No of Dependence</th>
<th>Policy Issue Date</th>
<th>Status</th>
<th>Action</th>
<th class="font-weight-medium">Sno&nbsp;</th>
<th class="font-weight-medium">Issuer&nbsp;</th>
<th class="font-weight-medium">Client&nbsp;</th>
<th class="font-weight-medium">Branch&nbsp;</th>
<th class="font-weight-medium">Insurer&nbsp;</th>
<th class="font-weight-medium">Policy&nbsp;</th>
<th class="font-weight-medium">Endorsement Type&nbsp;</th>
<th class="font-weight-medium">Endorsement No&nbsp;</th>
<th class="font-weight-medium">Data Recived Date&nbsp;</th>
<th class="font-weight-medium">No of Insured&nbsp;</th>
<th class="font-weight-medium">No of Dependence&nbsp;</th>
<th class="font-weight-medium">Policy Issue Date&nbsp;</th>
<th class="font-weight-medium">Status&nbsp;</th>
<th class="font-weight-medium text-center">Action&nbsp;</th>
</tr>
</thead>
<tbody>
@ -350,7 +309,7 @@
<td class="right-align-input"><?php echo $row['dependent_count'] ?: '0'; ?></td>
<td><?php echo empty($row['policy_issue_date']) ? 'N/A' : date('d/m/Y', strtotime($row['policy_issue_date'])); ?></td>
<td><?php echo $policy_status[$row['status']] ?: 'N/A'; ?></td>
<td>
<td class="text-center table-action-cell">
<div class="btn-group dropdown">
<a href="javascript: void(0);" class="dropdown-toggle arrow-none btn btn-light btn-sm" data-toggle="dropdown" aria-expanded="false"><i class="mdi mdi-dots-horizontal"></i></a>
<div class="dropdown-menu dropdown-menu-right">
@ -378,95 +337,13 @@
<?php include('pt_endorsement_form_file_upload_tab.php'); ?>
<script>
/* Modified by Gemini Code Assist */
function openEndorsementFilterNav() {
document.getElementById("filter-sidebar").style.width = "350px";
}
/* Modified by Gemini Code Assist */
function closeFilterNav() {
document.getElementById("filter-sidebar").style.width = "0";
}
document.addEventListener("DOMContentLoaded", function() {
const table = document.getElementById("tickets-table");
// Create custom dropdown
function createCustomDropdown(row) {
const originalDropdown = row.querySelector('.dropdown-menu');
if (!originalDropdown) return null;
const customDropdown = document.createElement('div');
customDropdown.className = 'custom-dropdown-menu';
customDropdown.innerHTML = originalDropdown.innerHTML;
// Remove inline onclick handlers and store them in data attributes
const originalItems = originalDropdown.querySelectorAll('.dropdown-item');
customDropdown.querySelectorAll('.dropdown-item').forEach((item, index) => {
const originalOnclick = originalItems[index].getAttribute('onclick');
item.removeAttribute('onclick'); // Remove the inline handler
item.setAttribute('data-onclick', originalOnclick); // Store in data attribute
});
return customDropdown;
}
let activeDropdown = null;
// Add click event listener to rows
table.querySelectorAll("tbody tr").forEach(row => {
const customDropdown = createCustomDropdown(row);
if (!customDropdown) return;
document.body.appendChild(customDropdown);
row.addEventListener("click", function(event) {
// Ignore clicks on the first column
if (event.target.closest('td:first-child')) return;
if (activeDropdown) activeDropdown.style.display = 'none';
const rect = event.target.getBoundingClientRect();
customDropdown.style.display = 'block';
customDropdown.style.position = 'fixed';
customDropdown.style.left = `${rect.left}px`;
customDropdown.style.top = `${rect.bottom + 5}px`;
activeDropdown = customDropdown;
event.stopPropagation();
});
// Handle custom dropdown clicks
customDropdown.querySelectorAll('.dropdown-item').forEach(item => {
item.addEventListener('click', function(e) {
e.preventDefault();
// Execute the original onclick from data attribute
const onclickAttr = this.getAttribute('data-onclick');
if (onclickAttr) eval(onclickAttr);
// Handle href navigation
const href = this.getAttribute('href');
if (href && href !== '#') window.location.href = href;
if (activeDropdown) {
activeDropdown.style.display = 'none';
activeDropdown = null;
}
e.stopPropagation();
});
});
});
// Close dropdown on outside click
document.addEventListener("click", function() {
if (activeDropdown) {
activeDropdown.style.display = 'none';
activeDropdown = null;
}
});
});
</script>
<script>
@ -516,7 +393,7 @@
var ticketsTable = $('#tickets-table');
if (ticketsTable.length) {
ticketsTable.DataTable({
var table = ticketsTable.DataTable({
scrollX: true,
dom: "<'row'<'col-sm-7'f><'col-sm-5 text-right'B>>" + // Filter left, buttons right
"<'row'<'col-sm-12'tr>>" +
@ -566,12 +443,34 @@
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666; display:none;"></i>
</div>`,
searchPlaceholder: "Search",
emptyTable: '<div class="text-center text-muted">No Data found</div>'
emptyTable: '<div class="text-center text-muted">No Data found</div>',
paginate: {
previous: '◄',
next: '►'
}
},
paging: true, // Enable pagination
pageLength: 10, // Set default number of rows per page (optional)
ordering: false,
});
function applyBottomRowDropup() {
if (!table) return;
const currentRows = table.rows({ page: 'current' }).nodes().toArray();
$('#tickets-table tbody tr').removeClass('nh-force-dropup');
$('#tickets-table tbody tr td.table-action-cell .btn-group.dropdown').removeClass('dropup');
const targetCount = Math.min(2, currentRows.length);
for (let i = 0; i < targetCount; i++) {
const row = currentRows[currentRows.length - 1 - i];
if (!row) continue;
$(row).addClass('nh-force-dropup');
$(row).find('td.table-action-cell .btn-group.dropdown').addClass('dropup');
}
}
applyBottomRowDropup();
ticketsTable.on('draw.dt', applyBottomRowDropup);
} else {
console.error("Table not found.");
}

View File

@ -3,70 +3,66 @@
<div class="col-12">
<div class="card-body subcard">
<div class="custom-form">
<form role="form" class="parsley-examples" method="post" id="policytype_general_form" enctype="multipart/form-data">
<form role="form" class="parsley-examples" method="post" id="policytype_general_form" enctype="multipart/form-data" data-parsley-validate>
<input type="hidden" value="<?= csrf_hash() ?>" name="<?= csrf_token() ?>" />
<input type="hidden" name="PrimaryKey" id="policytype_General_PrimaryKey" value="<?= isset($policytype['id']) ? $policytype['id'] : '' ?>"/>
<input type="hidden" name="policy_id" id="policy_id" />
<div class="form-group">
<div class="form-row">
<div class="form-group col-md-4">
<label for="kyc_name">Policy Type Name<span
class="text-danger">*</span></label>
<label for="policy_type">Policy Type Name<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="policy_type"
placeholder="Enter Policy Type Name" value="<?= isset($policytype['policy_type']) ? $policytype['policy_type'] : '' ?>" name="policy_type" required>
placeholder="Enter Policy Type Name" value="<?= isset($policytype['policy_type']) ? $policytype['policy_type'] : '' ?>" name="policy_type" required data-parsley-minlength="2">
</div>
<div class="form-group col-md-4">
<label for="kyc_name">Policy Type Long Name</label>
<label for="long_name">Policy Type Long Name</label>
<input type="text" class="form-control" id="long_name"
placeholder="Enter Policy Type Long Name" value="<?= isset($policytype['long_name']) ? $policytype['long_name'] : '' ?>" name="long_name">
</div>
<div class="form-group col-md-4">
<label for="bap">BAP<span
class="text-danger">*</span></label>
<label for="bap">BAP<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="bap"
placeholder="Enter Bap" value="<?= isset($policytype['bap']) ? $policytype['bap'] : '' ?>" name="bap" required>
</div>
<div class="form-group col-md-4">
<label for="allocg">Category<span
class="text-danger">*</span></label>
<label for="allocg">Category<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="allocg"
placeholder="Enter Allocg" value="<?= isset($policytype['allocg']) ? $policytype['allocg'] : '' ?>" name="allocg" required>
</div>
<div class="form-group col-md-4">
<label for="alloci">BAP Category<span
class="text-danger">*</span></label>
<label for="alloci">BAP Category<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="alloci"
placeholder="Enter Alloci" value="<?= isset($policytype['alloci']) ? $policytype['alloci'] : '' ?>" name="alloci" required>
</div>
<div class="form-group col-md-4">
<label for="ebp">Group Base Premium<span class="text-muted"> (STD %)</span></label>
<input type="number" class="form-control" id="ebp"
placeholder="Enter Group Base Premium" value="<?= isset($policytype['ebp']) ? $policytype['ebp'] : '' ?>" name="ebp">
placeholder="Enter Group Base Premium" value="<?= isset($policytype['ebp']) ? $policytype['ebp'] : '' ?>" name="ebp" data-parsley-type="number" data-parsley-min="0" data-parsley-min-message="Value cannot be negative">
</div>
<div class="form-group col-md-4">
<label for="etp">Group Third Party Premium<span
class="text-muted"> (STD %)</span></label> <input type="number" class="form-control" id="etp"
placeholder="Enter Group Base Premium" value="<?= isset($policytype['etp']) ? $policytype['etp'] : '' ?>" name="etp">
<label for="etp">Group Third Party Premium<span class="text-muted"> (STD %)</span></label>
<input type="number" class="form-control" id="etp"
placeholder="Enter Group Base Premium" value="<?= isset($policytype['etp']) ? $policytype['etp'] : '' ?>" name="etp" data-parsley-type="number" data-parsley-min="0" data-parsley-min-message="Value cannot be negative">
</div>
<div class="form-group col-md-4">
<label for="etep">Group Terrorism Premium<span
class="text-muted"> (STD %)</span></label> <input type="number" class="form-control" id="etep"
placeholder="Enter Group Base Premium" value="<?= isset($policytype['etep']) ? $policytype['etep'] : '' ?>" name="etep">
<label for="etep">Group Terrorism Premium<span class="text-muted"> (STD %)</span></label>
<input type="number" class="form-control" id="etep"
placeholder="Enter Group Base Premium" value="<?= isset($policytype['etep']) ? $policytype['etep'] : '' ?>" name="etep" data-parsley-type="number" data-parsley-min="0" data-parsley-min-message="Value cannot be negative">
</div>
<div class="form-group col-md-4">
<label for="iep">Individual Base Premium<span
class="text-muted"> (STD %)</span></label> <input type="number" class="form-control" id="iep"
placeholder="Enter Group Base Premium" value="<?= isset($policytype['iep']) ? $policytype['iep'] : '' ?>" name="iep">
<label for="iep">Individual Base Premium<span class="text-muted"> (STD %)</span></label>
<input type="number" class="form-control" id="iep"
placeholder="Enter Group Base Premium" value="<?= isset($policytype['iep']) ? $policytype['iep'] : '' ?>" name="iep" data-parsley-type="number" data-parsley-min="0" data-parsley-min-message="Value cannot be negative">
</div>
<div class="form-group col-md-4">
<label for="itp">Individual Third-Party Premium<span
class="text-muted"> (STD %)</span></label> <input type="number" class="form-control" id="itp"
placeholder="Enter Group Base Premium" value="<?= isset($policytype['itp']) ? $policytype['itp'] : '' ?>" name="itp">
<label for="itp">Individual Third-Party Premium<span class="text-muted"> (STD %)</span></label>
<input type="number" class="form-control" id="itp"
placeholder="Enter Group Base Premium" value="<?= isset($policytype['itp']) ? $policytype['itp'] : '' ?>" name="itp" data-parsley-type="number" data-parsley-min="0" data-parsley-min-message="Value cannot be negative">
</div>
<div class="form-group col-md-4">
<label for="itep">Individual Terrorism Premium<span
class="text-muted"> (STD %)</span></label> <input type="number" class="form-control" id="itep"
placeholder="Enter Group Base Premium" value="<?= isset($policytype['itep']) ? $policytype['itep'] : '' ?>" name="itep">
<label for="itep">Individual Terrorism Premium<span class="text-muted"> (STD %)</span></label>
<input type="number" class="form-control" id="itep"
placeholder="Enter Group Base Premium" value="<?= isset($policytype['itep']) ? $policytype['itep'] : '' ?>" name="itep" data-parsley-type="number" data-parsley-min="0" data-parsley-min-message="Value cannot be negative">
</div>
</div>
@ -148,35 +144,6 @@ $(document).ready(function () {
}
});
function validateForm() {
var isValid = true;
// Perform validation for each field
$('#policytype_general_form input, #policytype_general_form select').each(function() {
// Check for empty text inputs and selects
if ($(this).is('input[type="text"]') || $(this).is('select')) {
if ($.trim($(this).val()) == '') {
alert('Please fill in all fields');
isValid = false;
return false; // Exit the loop early
}
}
// Check for file inputs (assuming image files)
if ($(this).is('input[type="file"]')) {
var fileInput = $(this)[0];
if (fileInput.files.length === 0) {
alert('Please select an image file');
isValid = false;
return false; // Exit the loop early
}
}
});
return isValid;
}
});
</script>

View File

@ -98,14 +98,13 @@
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
</div>
<div class="modal-body p-4">
<!-- <form class="parsley-examples" id="partnerPOSForm" enctype="multipart/form-data"> -->
<form id="partnerPOSForm" enctype="multipart/form-data">
<form id="partnerPOSForm" enctype="multipart/form-data" data-parsley-validate>
<input type="hidden" name="pk" id="pos_id"/>
<div class="form-group">
<div class="form-row">
<div class="form-group col-md-12">
<label for="manager_id">Manager<span class="text-danger">*</span></label>
<select class="form-control" id="manager_id" name="manager_id" required>
<select class="form-control" id="manager_id" name="manager_id" required>
<option value="" selected>Select Manager</option>
<?php if (!empty($data['manager_list'])): ?>
<?php foreach ($data['manager_list'] as $value): ?>
@ -120,21 +119,19 @@
<div class="form-row">
<div class="form-group col-md-3">
<label for="name">Name<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="name" name="name" placeholder="Enter Name" required>
<input type="text" class="form-control" id="name" name="name" placeholder="Enter Name" required data-parsley-pattern="^[a-zA-Z\s\.]+$" data-parsley-trigger="keyup" data-parsley-pattern-message="Please enter a valid name (letters, spaces, dots only).">
</div>
<div class="form-group col-md-3">
<label for="pos_code">POS Code<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="pos_code" name="pos_code" placeholder="Enter Code" required>
<input type="text" class="form-control" id="pos_code" name="pos_code" placeholder="Enter Code" required data-parsley-pattern="^[a-zA-Z0-9_-]+$" data-parsley-trigger="keyup" data-parsley-pattern-message="Please enter a valid code (alphanumeric, underscore, hyphen).">
</div>
<div class="form-group col-md-3">
<label for="email">Email<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="email" name="email" placeholder="Enter Email" required>
<small id="email_error" class="text-danger d-none"></small>
<input type="email" class="form-control" id="email" name="email" placeholder="Enter Email" required data-parsley-type="email" data-parsley-trigger="keyup">
</div>
<div class="form-group col-md-3">
<label for="mobile">Mobile<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="mobile" name="mobile" placeholder="Enter Mobile" maxlength="10" inputmode="numeric" pattern="[0-9]{10}" required>
<small id="mobile_error" class="text-danger d-none"></small>
<input type="text" class="form-control" id="mobile" name="mobile" placeholder="Enter Mobile" required data-parsley-pattern="^[6-9]\d{9}$" data-parsley-trigger="keyup" data-parsley-length="[10,10]" data-parsley-validation-threshold="10" data-parsley-length-message="Mobile number must be 10 digits." data-parsley-pattern-message="Please enter a valid 10-digit mobile number starting with 6, 7, 8, or 9.">
</div>
</div>
<div class="form-row">
@ -152,44 +149,42 @@
</div>
<div class="form-group col-md-3">
<label for="pincode">Pincode<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="pincode" name="pincode" placeholder="Enter Pincode" required>
<input type="text" class="form-control" id="pincode" name="pincode" placeholder="Enter Pincode" required data-parsley-type="digits" data-parsley-length="[6,6]" data-parsley-trigger="keyup" data-parsley-validation-threshold="6" data-parsley-length-message="Pincode must be 6 digits.">
</div>
</div>
<div class="form-row">
<div class="form-group col-md-4">
<label for="aadhar">Aadhaar Number<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="aadhar" name="aadhar" placeholder="Enter Aadhaar Number" maxlength="12" inputmode="numeric" pattern="[0-9]{12}" required>
<small id="aadhar_error" class="text-danger d-none"></small>
<input type="text" class="form-control" id="aadhar" name="aadhar" placeholder="Enter Aadhaar Number" required data-parsley-type="digits" data-parsley-length="[12,12]" data-parsley-length-message="Aadhaar number must be 12 digits." data-parsley-trigger="keyup">
</div>
<div class="form-group col-md-4">
<label for="pan">PAN Number<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="pan" name="pan" placeholder="Enter PAN Number" maxlength="10" style="text-transform:uppercase" pattern="[A-Z]{5}[0-9]{4}[A-Z]{1}" required>
<small id="pan_error" class="text-danger d-none"></small>
<input type="text" class="form-control" id="pan" name="pan" placeholder="Enter PAN Number" style="text-transform:uppercase" required data-parsley-pattern="[A-Z]{5}[0-9]{4}[A-Z]{1}" data-parsley-maxlength="10" data-parsley-maxlength-message="PAN number cannot exceed 10 characters." data-parsley-pattern-message="Invalid PAN format (e.g., ABCDE1234F)." data-parsley-trigger="keyup">
</div>
<div class="form-group col-md-4">
<label for="gst">GST Number</label>
<input type="text" class="form-control" id="gst" name="gst" placeholder="Enter GST Number">
<input type="text" class="form-control" id="gst" name="gst" placeholder="Enter GST Number" data-parsley-pattern="/^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}Z[0-9A-Z]{1}$/" data-parsley-maxlength="15" data-parsley-maxlength-message="GST number cannot exceed 15 characters." data-parsley-pattern-message="Invalid GST format." data-parsley-trigger="keyup">
</div>
</div>
<div class="form-row">
<div class="form-group col-md-4">
<label for="aadhar_file_name">Aadhar</label>
<div class="input-icon">
<input type="file" class="form-control" name="aadhar_file_name" id="aadhar_file_name" accept=".pdf,.jpg,.jpeg,.png">
<input type="file" class="form-control" name="aadhar_file_name" id="aadhar_file_name" accept=".pdf,.jpg,.jpeg,.png" data-parsley-file-validation>
<i class="mdi mdi-upload additional-icon"></i>
</div>
</div>
<div class="form-group col-md-4">
<label for="pan_file_name">PAN</label>
<div class="input-icon">
<input type="file" class="form-control" name="pan_file_name" id="pan_file_name" accept=".pdf,.jpg,.jpeg,.png">
<input type="file" class="form-control" name="pan_file_name" id="pan_file_name" accept=".pdf,.jpg,.jpeg,.png" data-parsley-file-validation>
<i class="mdi mdi-upload additional-icon"></i>
</div>
</div>
<div class="form-group col-md-4">
<label for="certificate_file_name">Certificate</label>
<div class="input-icon">
<input type="file" class="form-control" name="certificate_file_name" id="certificate_file_name" accept=".pdf,.jpg,.jpeg,.png">
<input type="file" class="form-control" name="certificate_file_name" id="certificate_file_name" accept=".pdf,.jpg,.jpeg,.png" data-parsley-file-validation>
<i class="mdi mdi-upload additional-icon"></i>
</div>
</div>
@ -229,6 +224,41 @@
</div>
<script>
// Parsley custom validator for file type and size
window.Parsley.addValidator('fileValidation', {
requirementType: 'string',
validateString: function(value, requirement, parsleyInstance) {
const file = parsleyInstance.$element[0].files[0];
if (!file) {
return true; // Skip validation if no file is selected (unless 'required' is also present)
}
const maxImageSize = 500 * 1024; // 500KB
const maxPdfSize = 25 * 1024 * 1024; // 25MB
const imageTypes = ['image/jpeg', 'image/png', 'image/jpg'];
const pdfType = 'application/pdf';
if (imageTypes.includes(file.type)) {
if (file.size > maxImageSize) {
this.errorMessage = 'Image size cannot exceed 500KB.';
return false;
}
} else if (file.type === pdfType) {
if (file.size > maxPdfSize) {
this.errorMessage = 'PDF size cannot exceed 25MB.';
return false;
}
} else {
this.errorMessage = 'Allowed file types are JPG, JPEG, PNG, and PDF.';
return false;
}
return true;
},
messages: {
en: 'File is invalid.' // Default fallback message
}
});
var table;
$(document).ready(function () {
$('#manager_id').select2();
@ -285,7 +315,12 @@
});
});
$('.close').click(function(){ resetValues(); })
$('.close').click(function(){
resetValues();
if ($('#partnerPOSForm').parsley()) {
$('#partnerPOSForm').parsley().reset();
}
})
function openModal(){
var myModal = new bootstrap.Modal(document.getElementById('con-close-modal'));
@ -396,6 +431,9 @@
function resetValues(){
$('#modalLabel').text('Add POS Details');
$('#partnerPOSForm')[0].reset();
if ($('#partnerPOSForm').parsley()) {
$('#partnerPOSForm').parsley().reset();
}
$('#manager_id').val(null).trigger('change');
}
@ -452,81 +490,18 @@
$('#partnerPOSForm').on('submit', function(e) {
e.preventDefault();
const form = this;
var $form = $(this);
// Validate the form using Parsley
$form.parsley().validate();
// HTML5 built-in validation
if (!form.checkValidity()) {
form.reportValidity(); // shows required/pattern tooltips
return;
// Check if the form is valid
if ($form.parsley().isValid()) {
// If valid, submit via AJAX
handleSaveEditAndDelete('submit');
}
// Custom validation
if (!validateForm()) return;
// If valid, submit via AJAX
handleSaveEditAndDelete('submit');
});
function validateForm() {
let isValid = true;
// Clear old errors
$('.text-danger').addClass('d-none').text('');
// Regex patterns
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const mobileRegex = /^[6-9]\d{9}$/;
const aadharRegex = /^\d{12}$/;
const panRegex = /^[A-Z]{5}[0-9]{4}[A-Z]$/;
// EMAIL
const email = $('#email').val().trim();
if (email === '') {
$('#email_error').text('Email is required').removeClass('d-none');
isValid = false;
} else if (!emailRegex.test(email)) {
$('#email_error').text('Enter a valid email').removeClass('d-none');
isValid = false;
}
// MOBILE
const mobile = $('#mobile').val().trim();
if (mobile === '') {
$('#mobile_error').text('Mobile number is required').removeClass('d-none');
isValid = false;
} else if (!mobileRegex.test(mobile)) {
$('#mobile_error').text('Enter a valid 10-digit mobile number').removeClass('d-none');
isValid = false;
}
// AADHAAR
const aadhar = $('#aadhar').val().trim();
if (aadhar === '') {
$('#aadhar_error').text('Aadhaar is required').removeClass('d-none');
isValid = false;
} else if (!aadharRegex.test(aadhar)) {
$('#aadhar_error').text('Aadhaar must be 12 digits').removeClass('d-none');
isValid = false;
}
// PAN
const pan = $('#pan').val().trim();
if (pan === '') {
$('#pan_error').text('PAN is required').removeClass('d-none');
isValid = false;
} else if (!panRegex.test(pan)) {
$('#pan_error').text('Invalid PAN format (AAAPA1234A)').removeClass('d-none');
isValid = false;
}
return isValid;
}
</script>
<script>
document.addEventListener("DOMContentLoaded", function() {
@ -616,5 +591,5 @@
}
});
});
</script>
</script>

View File

@ -138,7 +138,7 @@
<h4 style="position: relative;">Endorsement</h4>
</div>
</div> -->
<table data-custom-table-css="table" class="table table-hover m-0 table-centered dt-responsive w-100" cellspacing="0" id="tickets-table">
<table data-custom-table-css="table" class="table table-hover m-0 table-centered w-100" cellspacing="0" id="tickets-table">
<thead class="bg-light">
<tr>
<th class="font-weight-medium">S.No&nbsp;</th>
@ -151,7 +151,7 @@
<th class="font-weight-medium">Mananger&nbsp;</th>
<th class="font-weight-medium">Created At&nbsp;</th>
<th class="font-weight-medium">Status&nbsp;</th>
<th class="font-weight-medium">Action&nbsp;</th>
<th class="font-weight-medium text-center">Action&nbsp;</th>
</tr>
</thead>
@ -182,8 +182,15 @@
}
?>
</td>
<td class="text-center"><a href="#" data-id="<?php echo $employee['id'] ?>" data-eno="<?php echo $employee['endorsement_no'] ?>" data-status="<?php echo $employee['status'] ?>"
class="mdi mdi-pencil text-muted vertical-middle emp_data_model" aria-hidden="true" data-toggle="modal" data-target="#centermodal"></a>
<td class="text-center table-action-cell">
<div class="btn-group dropdown">
<a href="javascript: void(0);" class="dropdown-toggle arrow-none btn btn-light btn-sm" data-toggle="dropdown" aria-expanded="false"><i class="mdi mdi-dots-horizontal"></i></a>
<div class="dropdown-menu dropdown-menu-right">
<a class="dropdown-item emp_data_model" href="#" data-id="<?php echo $employee['id'] ?>" data-eno="<?php echo $employee['endorsement_no'] ?>" data-status="<?php echo $employee['status'] ?>" data-toggle="modal" data-target="#centermodal">
<i class="mdi mdi-pencil mr-2 text-muted font-18 vertical-middle"></i>Edit
</a>
</div>
</div>
</td>
</tr>
<?php }
@ -398,7 +405,8 @@
$(document).ready(function() {
$('#tickets-table').DataTable({
var table = $('#tickets-table').DataTable({
scrollX: true,
dom: "<'row'<'col-12 d-flex justify-content-between align-items-center'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'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>>",
@ -453,7 +461,11 @@
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666; display:none;"></i>
</div>`,
searchPlaceholder: "Search",
emptyTable: '<div class="text-center text-muted">No Data found</div>'
emptyTable: '<div class="text-center text-muted">No Data found</div>',
paginate: {
previous: '◄',
next: '►'
}
},
paging: true,
// pagingType: 'full_numbers'
@ -464,6 +476,25 @@
],
});
function applyBottomRowDropup() {
if (!table) return;
const currentRows = table.rows({ page: 'current' }).nodes().toArray();
$('#tickets-table tbody tr').removeClass('nh-force-dropup');
$('#tickets-table tbody tr td.table-action-cell .btn-group.dropdown').removeClass('dropup');
// Mark last two rows on current page to open action menu upward.
const targetCount = Math.min(2, currentRows.length);
for (let i = 0; i < targetCount; i++) {
const row = currentRows[currentRows.length - 1 - i];
if (!row) continue;
$(row).addClass('nh-force-dropup');
$(row).find('td.table-action-cell .btn-group.dropdown').addClass('dropup');
}
}
applyBottomRowDropup();
$('#tickets-table').on('draw.dt', applyBottomRowDropup);
});

View File

@ -72,37 +72,53 @@
</div>
<div class="modal-body p-4">
<!-- <form class="parsley-examples" id="RTOForm" enctype="multipart/form-data"> -->
<form id="RTOForm" enctype="multipart/form-data">
<form id="RTOForm" enctype="multipart/form-data" data-parsley-validate>
<input type="hidden" name="pk" id="rto_id"/>
<div class="form-group">
<div class="form-row">
<div class="form-group col-md-12">
<label for="rto_name">RTO Office Name<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="rto_name" name="rto_name" placeholder="Enter RTO Office Name" required>
<input type="text" class="form-control" id="rto_name" name="rto_name" placeholder="Enter RTO Office Name"
required
data-parsley-required-message="RTO Office Name is required"
data-parsley-minlength="3"
data-parsley-minlength-message="RTO Office Name must be at least 3 characters"
data-parsley-maxlength="100"
data-parsley-maxlength-message="RTO Office Name maximum 100 characters"
data-parsley-pattern="^[a-zA-Z0-9\s]+$"
data-parsley-pattern-message="RTO Office Name may only contain alphanumeric and space characters"
data-parsley-trigger="keyup">
</div>
<div class="form-group col-md-12">
<label for="rto_code">RTO Code<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="rto_code" name="rto_code"
placeholder="Enter RTO Code" required
maxlength="2"
inputmode="numeric"
pattern="[0-9]{2}"
oninput="this.value = this.value.replace(/[^0-9]/g, '').slice(0,2);">
placeholder="Enter RTO Code"
required
data-parsley-required-message="RTO Code is required"
data-parsley-type="digits"
data-parsley-type-message="RTO Code must contain only numbers"
data-parsley-length="[2,2]"
data-parsley-length-message="RTO Code must be exactly 2 digits"
data-parsley-trigger="keyup">
</div>
<div class="form-group col-md-12">
<label for="rto_state">RTO State<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="rto_state" name="rto_state"
placeholder="Enter RTO State" required
maxlength="2"
pattern="[A-Z]{2}"
oninput="this.value = this.value.replace(/[^A-Za-z]/g, '').toUpperCase().slice(0,2);">
placeholder="Enter RTO State" style="text-transform:uppercase"
required
data-parsley-required-message="RTO State is required"
data-parsley-type="alpha"
data-parsley-type-message="RTO State must contain only alphabets"
data-parsley-length="[2,2]"
data-parsley-length-message="RTO State must be exactly 2 letters"
data-parsley-trigger="keyup">
</div>
</div>
</div>
<div class="form-group text-right m-b-0">
<button type="button" class="btn app-btn-secondary waves-effect waves-light" id="btnSubmit" onclick="handleSaveEditAndDelete('submit')">Submit</button>
<button type="submit" class="btn app-btn-secondary waves-effect waves-light" id="btnSubmit">Submit</button>
</div>
</form>
</div>
@ -167,7 +183,10 @@
});
$('.close').click(function(){
resetValues()
resetValues();
if ($('#RTOForm').parsley()) {
$('#RTOForm').parsley().reset();
}
})
function openModal(){
@ -273,6 +292,9 @@
function resetValues(){
$('#modalLabel').text('Add RTO Details');
$('#RTOForm')[0].reset();
if ($('#RTOForm').parsley()) {
$('#RTOForm').parsley().reset();
}
}
function appendEditData(data){
@ -282,5 +304,16 @@
$('#rto_state').val(data[0]['rto_state']);
openModal()
}
</script>
$('#RTOForm').on('submit', function(e) {
e.preventDefault();
var $form = $(this);
// Validate the form using Parsley
$form.parsley().validate();
if ($form.parsley().isValid()) {
handleSaveEditAndDelete('submit');
}
});
</script>

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,8 @@
<style>
.col-12 {
max-width: 98% !important;
}
.badge2 {
box-shadow: none;
}
@ -31,7 +35,7 @@
}
.dataTables_filter{
position: absolute;
position: relative;
}
.right-align-input {
@ -60,38 +64,6 @@
#map_emp_modal .modal-backdrop.show {
opacity: 0.5; /* Darken backdrop for better focus */
}
.custom-dropdown-menu {
display: none;
position: absolute;
background-color: #ffffff !important;
border: 1px solid rgba(0, 0, 0, 0.15);
border-radius: 0.25rem;
padding: 0.5rem 0;
min-width: 10rem;
z-index: 9999;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.custom-dropdown-menu .dropdown-item {
display: block !important;
width: 100% !important;
padding: 0.5rem 1rem !important;
color: #212529 !important;
text-decoration: none !important;
background-color: transparent !important;
}
.custom-dropdown-menu .dropdown-item:hover {
background-color: #f8f9fa !important;
color: #16181b !important;
cursor: pointer !important;
}
th, td {
text-align: center; /* Or left/right depending on your preference */
vertical-align: middle; /* Ensures content is vertically centered */
}
th:first-child, td:first-child {
width: 50px; /* Adjust width as needed */
}
@ -99,8 +71,22 @@ th:first-child, td:first-child {
overflow-x: auto;
}
/* Allow header text and sort icons to keep proper spacing. */
.table {
table-layout: fixed; /* Prevent columns from resizing dynamically */
table-layout: auto;
}
/* Keep this long header readable and prevent sort icon overlap. */
#tickets-table th.existing-policy-col,
#tickets-table td.existing-policy-col {
min-width: 170px;
white-space: nowrap;
}
/* Action column aligned center in both head and body. */
#tickets-table th.action-col {
min-width: 90px;
text-align: center !important;
}
.unmapEmployees{
@ -135,7 +121,7 @@ th:first-child, td:first-child {
<h3 style="position: relative;">Employees</h3>
</div>
</div> -->
<table data-custom-table-css="table" class="table table-striped mb-0 nowrap" cellspacing="0" id="tickets-table">
<table data-custom-table-css="table" class="table mb-0 nowrap" cellspacing="0" id="tickets-table">
<thead class="bg-light">
<tr>
<th><input type="checkbox" id="selectAll" onclick="toggleAll(this)">&nbsp;</th>
@ -153,7 +139,7 @@ th:first-child, td:first-child {
<th class="font-weight-medium">Relationship&nbsp;</th>
<th class="font-weight-medium">Gender&nbsp;</th>
<th class="font-weight-medium">Date of Birth&nbsp;</th>
<th class="font-weight-medium">Existing Policy Count&nbsp;</th>
<th class="font-weight-medium existing-policy-col">Existing Policy Count&nbsp;</th>
<!-- <th class="font-weight-medium">Policy name</th>
<th class="font-weight-medium">Insurer name</th>
<th class="font-weight-medium">TPA ID</th>
@ -163,7 +149,7 @@ th:first-child, td:first-child {
<th class="font-weight-medium">()Premium</th>
<th class="font-weight-medium" id="rata_premium" data-toggle="tooltip" data-placement="top">()Pro Rata <br> Premium</th>
<th class="font-weight-medium" id="gst" data-toggle="tooltip" data-placement="top">()GST</th> -->
<th class="font-weight-medium" > Action </th>
<th class="font-weight-medium text-center action-col">Action&nbsp;</th>
</tr>
</thead>
@ -228,8 +214,8 @@ th:first-child, td:first-child {
<td><?php // if(isset($employee['gst'])){$gst_total += $employee['gst']; echo format_indian_number($employee['gst']); }?></td> -->
*/
?>
<td><?= $employee['policy_count'] ?></td>
<td>
<td class="existing-policy-col"><?= $employee['policy_count'] ?></td>
<td class="table-action-cell">
<div class="btn-group dropdown">
<a href="javascript: void(0);" class="dropdown-toggle arrow-none btn btn-light btn-sm" data-toggle="dropdown" aria-expanded="false"><i class="mdi mdi-dots-horizontal"></i></a>
<div class="dropdown-menu dropdown-menu-right">
@ -428,88 +414,6 @@ th:first-child, td:first-child {
</div>
</div><!-- /.modal -->
<script>
document.addEventListener("DOMContentLoaded", function () {
const table = document.getElementById("tickets-table");
// Create custom dropdown
function createCustomDropdown(row) {
const originalDropdown = row.querySelector('.dropdown-menu');
if (!originalDropdown) return null;
const customDropdown = document.createElement('div');
customDropdown.className = 'custom-dropdown-menu';
customDropdown.innerHTML = originalDropdown.innerHTML;
// Remove inline onclick handlers and store them in data attributes
const originalItems = originalDropdown.querySelectorAll('.dropdown-item');
customDropdown.querySelectorAll('.dropdown-item').forEach((item, index) => {
const originalOnclick = originalItems[index].getAttribute('onclick');
item.removeAttribute('onclick'); // Remove the inline handler
item.setAttribute('data-onclick', originalOnclick); // Store in data attribute
});
return customDropdown;
}
let activeDropdown = null;
// Add click event listener to rows
table.querySelectorAll("tbody tr").forEach(row => {
const customDropdown = createCustomDropdown(row);
if (!customDropdown) return;
document.body.appendChild(customDropdown);
row.addEventListener("click", function(event) {
// Ignore clicks on the first column
if (event.target.closest('td:first-child')) return;
if (activeDropdown) activeDropdown.style.display = 'none';
const rect = event.target.getBoundingClientRect();
customDropdown.style.display = 'block';
customDropdown.style.position = 'fixed';
customDropdown.style.left = `${rect.left}px`;
customDropdown.style.top = `${rect.bottom + 5}px`;
activeDropdown = customDropdown;
event.stopPropagation();
});
// Handle custom dropdown clicks
customDropdown.querySelectorAll('.dropdown-item').forEach(item => {
item.addEventListener('click', function(e) {
e.preventDefault();
// Execute the original onclick from data attribute
const onclickAttr = this.getAttribute('data-onclick');
if (onclickAttr) eval(onclickAttr);
// Handle href navigation
const href = this.getAttribute('href');
if (href && href !== '#') window.location.href = href;
if (activeDropdown) {
activeDropdown.style.display = 'none';
activeDropdown = null;
}
e.stopPropagation();
});
});
});
// Close dropdown on outside click
document.addEventListener("click", function() {
if (activeDropdown) {
activeDropdown.style.display = 'none';
activeDropdown = null;
}
});
});
</script>
<script>
function toggleAll(selectAllCheckbox) {
const rowCheckboxes = document.querySelectorAll('.rowCheckbox');
@ -538,7 +442,7 @@ document.addEventListener("DOMContentLoaded", function () {
var ticketsTable = $('#tickets-table');
if (ticketsTable.length) {
ticketsTable.DataTable({
var table = ticketsTable.DataTable({
scrollX: true,
// dom: "<'row'<'col-sm-6'f><'col-sm-6 text-right'B>>" + // Filter left, buttons right
// "<'row'<'col-sm-12'tr>>" +
@ -552,14 +456,14 @@ document.addEventListener("DOMContentLoaded", function () {
action: function(e, dt, node, config) {
mapEmployees();
},
className:"mapEmployees"
className:"btn app-btn-primary mr-2 mapEmployees"
},
{
text:'Unmap Employees',
action: function(e, dt, node, config) {
unmapEmployees();
},
className:"unmapEmployees"
className:"btn app-btn-primary unmapEmployees"
}],
language: {
search: `
@ -571,11 +475,34 @@ document.addEventListener("DOMContentLoaded", function () {
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666; display:none;"></i>
</div>`,
searchPlaceholder: "Search",
emptyTable: '<div class="text-center text-muted">No Data found</div>'
emptyTable: '<div class="text-center text-muted">No Data found</div>',
paginate: {
previous: '◄',
next: '►'
}
},
paging: true, // Enable pagination
pageLength: 25 // Set default number of rows per page (optional)
pageLength: 10 // Set default number of rows per page (optional)
});
function applyBottomRowDropup() {
if (!table) return;
const currentRows = table.rows({ page: 'current' }).nodes().toArray();
$('#tickets-table tbody tr').removeClass('nh-force-dropup');
$('#tickets-table tbody tr td.table-action-cell .btn-group.dropdown').removeClass('dropup');
// Mark last two rows on current page to open action menu upward.
const targetCount = Math.min(2, currentRows.length);
for (let i = 0; i < targetCount; i++) {
const row = currentRows[currentRows.length - 1 - i];
if (!row) continue;
$(row).addClass('nh-force-dropup');
$(row).find('td.table-action-cell .btn-group.dropdown').addClass('dropup');
}
}
applyBottomRowDropup();
$('#tickets-table').on('draw.dt', applyBottomRowDropup);
} else {
console.error("Table not found.");
}

View File

@ -167,6 +167,7 @@ beccause = dataTables_length and dataTables_paginate need in same line thats why
required pattern="[0-9]{10}" maxlength="10" inputmode="numeric"
oninput="this.value=this.value.replace(/[^0-9]/g,'');"
title="Please enter a valid 10-digit mobile number">
<small class="text-danger d-block mt-1 field-error" id="mobile_error"></small>
</div>
<div class="form-group col-md-1" style="padding:32.5px 0 0 18px;">
@ -186,6 +187,7 @@ beccause = dataTables_length and dataTables_paginate need in same line thats why
<div class="form-group col-md-4 ">
<label for="name">Name<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="name" name="name" placeholder="Enter Name" required>
<small class="text-danger d-block mt-1 field-error" id="name_error"></small>
</div>
<div class="form-group col-md-4 ">
@ -194,11 +196,13 @@ beccause = dataTables_length and dataTables_paginate need in same line thats why
name="email" placeholder="Enter Email"
required
title="Please enter a valid email address">
<small class="text-danger d-block mt-1 field-error" id="email_error"></small>
</div>
<div class="form-group col-md-4 ">
<label for="empcode">Employee code</label>
<input type="text" class="form-control" id="empcode" name="empcode" placeholder="Enter Employee code">
<small class="text-danger d-block mt-1 field-error" id="empcode_error"></small>
</div>
@ -218,6 +222,7 @@ beccause = dataTables_length and dataTables_paginate need in same line thats why
<?php endforeach; ?>
<?php endif; ?>
</select>
<small class="text-danger d-block mt-1 field-error" id="ticket_type_error"></small>
</div>
<div class="form-group col-md-4 " id="policy_container">
@ -254,6 +259,7 @@ beccause = dataTables_length and dataTables_paginate need in same line thats why
<?php endforeach; ?>
<?php endif; ?>
</select>
<small class="text-danger d-block mt-1 field-error" id="assign_to_error"></small>
</div>
<?php else: ?>
<input type="hidden" id="hide_assign_to" name="assign_to" value="<?= esc(get_session_userid()); ?>">
@ -269,6 +275,7 @@ beccause = dataTables_length and dataTables_paginate need in same line thats why
<small id="subjectCounter" class="form-text text-muted">0/150</small>
</div>
<input type="text" class="form-control" id="subject" name="subject" placeholder="Enter Subject" required maxlength="150">
<small class="text-danger d-block mt-1 field-error" id="subject_error"></small>
</div>
<div class="form-group col-md-12 ">
@ -278,6 +285,7 @@ beccause = dataTables_length and dataTables_paginate need in same line thats why
</div>
<!-- <input type="text" class="form-control" id="message" name="message" placeholder="Enter Message"> -->
<textarea class="form-control" id="message" name="message" placeholder="Enter Message" rows="3" required maxlength="1500"></textarea>
<small class="text-danger d-block mt-1 field-error" id="message_error"></small>
</div>
</div>
@ -406,6 +414,7 @@ beccause = dataTables_length and dataTables_paginate need in same line thats why
form.reset();
$("#client_id").select2();
$("#ticket_type").select2();
clearAllFieldErrors();
document.getElementById('thz_id').value = '';
document.getElementById('modalLabel').innerText = "New Ticket";
document.getElementById('return_type').innerText = "Web";
@ -418,6 +427,125 @@ beccause = dataTables_length and dataTables_paginate need in same line thats why
form.reset();
$("#client_id").select2();
$("#ticket_type").select2();
clearAllFieldErrors();
}
function showFieldError(fieldId, message) {
var field = document.getElementById(fieldId);
var error = document.getElementById(fieldId + "_error");
if (!field || !error) return;
error.textContent = message || "";
if (message) {
field.classList.add("is-invalid");
} else {
field.classList.remove("is-invalid");
}
}
function clearAllFieldErrors() {
document.querySelectorAll(".field-error").forEach(function (el) {
el.textContent = "";
});
["mobile", "name", "email", "empcode", "ticket_type", "assign_to", "subject", "message"].forEach(function (id) {
var field = document.getElementById(id);
if (field) field.classList.remove("is-invalid");
});
}
function validateMobile(value) {
if (!value) return "Mobile number is required";
if (!/^[0-9]{10}$/.test(value)) return "Mobile number must be exactly 10 digits";
return "";
}
function validateName(value) {
if (!value) return "Name is required";
if (value.length < 3) return "Name must be at least 3 characters";
if (value.length > 100) return "Name must be at most 100 characters";
if (!/^[A-Za-z ]+$/.test(value)) return "Name can contain only letters and spaces";
return "";
}
function validateEmail(value) {
if (!value) return "Email address is required.";
if (!/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(value)) {
return "Please enter a valid email format (e.g., name@domain.com).";
}
return "";
}
function validateEmpcode(value) {
if (!value) return "";
if (value.length > 50) return "Employee code is too long";
if (!/^[A-Za-z0-9 ~!#\$%\^&\*\(\)_\+\-\=\{\}\[\]\|:;\"'<>,\.\/\?`]+$/.test(value)) {
return "Employee code contains invalid characters.";
}
return "";
}
function validateTicketType(value) {
if (!value) return "Ticket Type is required";
return "";
}
function validateAssignTo(value) {
if (!value) return "";
if (!/^\d+$/.test(value)) return "Invalid assignee selected";
return "";
}
function validateSubject(value) {
if (!value) return "Subject is required.";
if (value.length < 3) return "Subject must be at least 3 characters long.";
if (value.length > 150) return "Subject cannot exceed 150 characters";
if (!/^[a-zA-Z0-9 .,\-_()&\/\?]+$/.test(value)) {
return "Subject contains invalid characters (Avoid quotes and special symbols).";
}
return "";
}
function validateMessage(value) {
if (!value) return "Message is required";
if (value.length < 10) return "Message must be at least 10 characters";
if (value.length > 1500) return "Message cannot exceed 1500 characters";
return "";
}
function validateField(fieldId) {
var field = document.getElementById(fieldId);
if (!field) return true;
var value = (field.value || "").trim();
var error = "";
switch (fieldId) {
case "mobile": error = validateMobile(value); break;
case "name": error = validateName(value); break;
case "email": error = validateEmail(value); break;
case "empcode": error = validateEmpcode(value); break;
case "ticket_type": error = validateTicketType(value); break;
case "assign_to": error = validateAssignTo(value); break;
case "subject": error = validateSubject(value); break;
case "message": error = validateMessage(value); break;
}
showFieldError(fieldId, error);
return !error;
}
function validateTicketForm() {
var fields = ["mobile", "name", "email", "empcode", "ticket_type", "subject", "message"];
if (document.getElementById("assign_to")) {
fields.push("assign_to");
}
var firstInvalid = null;
var isValid = true;
fields.forEach(function (fieldId) {
var ok = validateField(fieldId);
if (!ok && !firstInvalid) firstInvalid = document.getElementById(fieldId);
if (!ok) isValid = false;
});
if (!isValid && firstInvalid) firstInvalid.focus();
return isValid;
}
function submitTicket(event) {
@ -425,59 +553,8 @@ beccause = dataTables_length and dataTables_paginate need in same line thats why
event.preventDefault();
var form = document.getElementById('ticketForm');
// reset any old error styles
form.classList.remove('was-validated');
var name = document.getElementById('name').value.trim();
var mobile = document.getElementById('mobile').value.trim();
var email = document.getElementById('email').value.trim();
var message = document.getElementById('message').value.trim();
var subject = document.getElementById('subject').value.trim();
var ticketType = document.getElementById('ticket_type').value.trim();
// Validation checks
if (message.length === 0) {
toastr.warning('Message cannot be empty', 'Warning');
form.classList.add('was-validated');
return;
}
if (message.length > 1500) {
toastr.warning('Message cannot exceed 1500 characters', 'Warning');
form.classList.add('was-validated');
return;
}
if (subject.length === 0) {
toastr.warning('Subject cannot be empty', 'Warning');
form.classList.add('was-validated');
return;
}
if (subject.length > 150) {
toastr.warning('Subject cannot exceed 150 characters', 'Warning');
form.classList.add('was-validated');
return;
}
var mobilePattern = /^[0-9]{10}$/;
if (!mobilePattern.test(mobile)) {
toastr.warning('Please enter a valid 10-digit mobile number.', 'Warning');
document.getElementById('mobile').focus();
return;
}
var emailPattern = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
if (!emailPattern.test(email)) {
toastr.warning('Please enter a valid email address.', 'Warning');
document.getElementById('email').focus();
return;
}
if (name === "" || mobile === "" || email === "" || ticketType === "" || subject === "" || message === "") {
toastr.warning('Please fill all required fields.', 'Warning');
form.classList.add('was-validated');
if (!validateTicketForm()) {
toastr.warning('Please correct highlighted fields.', 'Warning');
return;
}
@ -509,10 +586,12 @@ beccause = dataTables_length and dataTables_paginate need in same line thats why
console.error(xhr.responseText);
if (xhr.status === 400) {
let response = JSON.parse(xhr.responseText);
clearAllFieldErrors();
let errorMessages = "";
let seenMessages = []; // Array to store unique messages
if (response.errors) {
$.each(response.errors, function (field, message) {
showFieldError(field, message);
if (!seenMessages.includes(message)) {
errorMessages += `• ${message}<br>`;
seenMessages.push(message); // Mark this message as "seen"
@ -719,11 +798,11 @@ $(document).ready(function() {
subjectCounter.textContent = `${subjectLength}/150`;
if (subjectLength > 150) {
toastr.warning('Subject cannot exceed 150 characters', 'Warning');
subjectInput.classList.add('is-invalid');
} else {
subjectInput.classList.remove('is-invalid');
}
validateField("subject");
});
@ -735,11 +814,27 @@ $(document).ready(function() {
messageCounter.textContent = `${messageLength}/1500`;
if (messageLength > 1500) {
toastr.warning('Message cannot exceed 1500 characters', 'Warning');
messageInput.classList.add('is-invalid');
} else {
messageInput.classList.remove('is-invalid');
}
validateField("message");
});
[
{ id: "mobile", event: "input" },
{ id: "name", event: "input" },
{ id: "email", event: "input" },
{ id: "empcode", event: "input" },
{ id: "ticket_type", event: "change" },
{ id: "assign_to", event: "change" }
].forEach(function (cfg) {
var el = document.getElementById(cfg.id);
if (el) {
el.addEventListener(cfg.event, function () {
validateField(cfg.id);
});
}
});

View File

@ -169,19 +169,19 @@ hr{
</div>
<div class="card-body card-scroll " style="padding:0 27px !important; background:#F5FFFF; border-radius: 15px !important;">
<div class="row mb-2 ml-o">
<div class="col-4 app-subtitle app-text-grey ">Ticket ID</div>
<div class="col-4 app-text-grey " style="font-size: 14px;">Ticket ID</div>
<div class="col-8 font-color-black text-end " style="font-weight: 500 !important;"><?= $master[0]['thz_id'];?></div>
</div>
<div class="row mb-2">
<div class="col-4 app-subtitle app-text-grey ">Name</div>
<div class="col-4 app-text-grey " style="font-size: 14px;">Name</div>
<div class="col-8 font-color-black text-end " style="font-weight: 500 !important;"><?= $master[0]['name'];?></div>
</div>
<div class="row mb-2">
<div class="col-4 app-subtitle app-text-grey ">Mobile</div>
<div class="col-4 app-text-grey " style="font-size: 14px;">Mobile</div>
<div class="col-8 font-color-black text-end " style="font-weight: 500 !important;"><?= $master[0]['mobile'];?></div>
</div>
<div class="row mb-2">
<div class="col-4 app-subtitle app-text-grey ">Policy Number</div>
<div class="col-4 app-text-grey " style="font-size: 14px;">Policy Number</div>
<div class="col-8 text-end font-color-black" style="font-weight: 500 !important;">
<?= (!empty($master[0]['policy_no']) && strtolower($master[0]['policy_no']) !== 'null')
? '<u class="font-color-black">
@ -198,11 +198,11 @@ hr{
</div>
</div>
<div class="row mb-2">
<div class="col-4 app-subtitle app-text-grey ">Assigned To</div>
<div class="col-4 app-text-grey " style="font-size: 14px;">Assigned To</div>
<div class="col-8 font-color-black text-end " style="font-weight: 500 !important;"><?= $master[0]['assignee_name'];?></div>
</div>
<div class="row mb-2">
<div class="col-4 app-subtitle app-text-grey ">Status</div>
<div class="col-4 app-text-grey " style="font-size: 14px;">Status</div>
<div class="col-8 font-color-black text-end " style="font-weight: 500 !important;">
<?= (!empty($master[0]['status']) && strtolower($master[0]['status']) !== 'null')
? '<u class="font-color-black">
@ -219,19 +219,19 @@ hr{
</div>
</div>
<div class="row mb-2">
<div class="col-4 app-subtitle app-text-grey ">Ticket Type</div>
<div class="col-4 app-text-grey " style="font-size: 14px;">Ticket Type</div>
<div class="col-8 font-color-black text-end " style="font-weight: 500 !important;"><?= $master[0]['ticket_type'];?></div>
</div>
<div class="row mb-2">
<div class="col-4 app-subtitle app-text-grey ">Subject</div>
<div class="col-4 app-text-grey " style="font-size: 14px;">Subject</div>
<div class="col-8 font-color-black text-end " style="font-weight: 500 !important; text-align: justify;"><?= $master[0]['subject'];?></div>
</div>
<div class="row mb-2">
<div class="col-4 app-subtitle app-text-grey ">Message</div>
<div class="col-4 app-text-grey " style="font-size: 14px;">Message</div>
<div class="col-8 font-color-black text-end " style="font-weight: 500 !important; text-align: justify;"><?= $master[0]['message'];?></div>
</div>
<div class="row mb-2">
<div class="col-4 app-subtitle app-text-grey ">Related Tickets</div>
<div class="col-4 app-text-grey " style="font-size: 14px;">Related Tickets</div>
<div class="col-8 text-end " style="font-weight: 500 !important;">
<u class="text-warning">
<a href="javascript:void(0);"
@ -310,6 +310,7 @@ hr{
</div>
</div>
<small id="messageCounter" class="form-text text-muted text-end text-right">0/1500</small>
<small id="notes_error" class="text-danger d-block mt-1 field-error" role="alert"></small>
</form>
</div>
</div>
@ -482,6 +483,38 @@ data-backdrop="static"
<script>
function showNotesFieldError(message) {
var field = document.getElementById("notes");
var error = document.getElementById("notes_error");
if (!field || !error) return;
error.textContent = message || "";
if (message) {
field.classList.add("is-invalid");
} else {
field.classList.remove("is-invalid");
}
}
function clearNotesFieldError() {
showNotesFieldError("");
}
/** Mirrors ticketConversationSave rules for `notes` */
function validateNotesValue(raw) {
var v = raw == null ? "" : String(raw);
if (v.length > 1500) {
return "Notes cannot exceed 1500 characters";
}
var t = v.trim();
if (v.length === 0) {
return "Notes is required";
}
if (t.length === 0) {
return "Notes cannot be empty";
}
return "";
}
$(document).ready(function() {
// Initialize select2
$("#ticket_type, #status, #assign_to").select2();
@ -493,12 +526,8 @@ data-backdrop="static"
const messageLength = messageInput.value.length;
messageCounter.textContent = `${messageLength}/1500`;
if (messageLength > 1500) {
toastr.warning('Message cannot exceed 1500 characters', 'Warning');
messageInput.classList.add('is-invalid');
} else {
messageInput.classList.remove('is-invalid');
}
var err = validateNotesValue(messageInput.value);
showNotesFieldError(err);
});
const btn = document.querySelector("button[onclick='submitTicket()']");
@ -608,8 +637,6 @@ data-backdrop="static"
}
function submitConverstionTicket(notetype,btn) {
btn.disabled = true;
document.getElementById("notes_type").value = notetype;
const urlParams = new URLSearchParams(window.location.search);
document.getElementById("thz_id").value = urlParams.get('thz_id');
@ -618,14 +645,16 @@ data-backdrop="static"
// reset any old error styles
form.classList.remove('was-validated');
var notes = document.getElementById('notes').value.trim();
if (notes === "") {
toastr.warning('Please enter notes', 'Warning');
form.classList.add('was-validated');
var notesEl = document.getElementById('notes');
var notesErr = validateNotesValue(notesEl.value);
showNotesFieldError(notesErr);
if (notesErr) {
notesEl.focus();
return;
}
btn.disabled = true;
var formData = new FormData(form);
$.ajax({
@ -639,6 +668,9 @@ data-backdrop="static"
if (response.status === "success") {
toastr.success(response.message, 'Success');
form.reset();
clearNotesFieldError();
var mc = document.getElementById("messageCounter");
if (mc) mc.textContent = "0/1500";
window.location.href = "<?= base_url('ticketConversationList?return_type=web&thz_id=') ?>" + id;
} else {
toastr.error(response.message, 'Error');
@ -648,16 +680,10 @@ data-backdrop="static"
console.error(xhr.responseText);
if (xhr.status === 400) {
let response = JSON.parse(xhr.responseText);
let errorMessages = "";
let seenMessages = []; // Array to store unique messages
if (response.errors) {
$.each(response.errors, function (field, message) {
if (!seenMessages.includes(message)) {
errorMessages += `• ${message}<br>`;
seenMessages.push(message); // Mark this message as "seen"
}
});
toastr.error(errorMessages, 'Validation Error', { "allowHtml": true });
if (response.errors.notes) {
showNotesFieldError(response.errors.notes);
}
} else {
toastr.warning(response.message || 'Validation failed', 'Warning');
}
@ -689,7 +715,7 @@ function statusHistory(thzId) {
if (response.status === "success" && response.data.length > 0) {
let table = `
<div class="table-responsive">
<table data-custom-table-css="table" class="table w-100 nowrap text-custom-black text-custom app-datatable">
<table data-custom-table-css="table" class="table app-table-card w-100 nowrap text-custom-black text-custom app-datatable">
<thead class="bg-light">
<tr>
<th class="app-text-white">S.No</th>

View File

@ -262,7 +262,7 @@
"<'row align-items-center'<'col-5 text-start'i><'col-7 d-flex justify-content-end align-items-center'<'me-2'l>p>>",
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
buttons: [{
text: 'Add Template',
text: '<i class="mdi mdi-plus"></i><span class="btn-custom"> Add Template </span>',
className: 'buttons-html5 addbtnStyle',
action: function(e, dt, node, config) {
openModal('new_mail_template')

View File

@ -60,7 +60,7 @@
<div class="col-12">
<div class="card-body subcard">
<div class="custom-form">
<form role="form" class="parsley-examples" method="post" id="tpa_general_form" enctype="multipart/form-data">
<form role="form" class="parsley-examples" method="post" id="tpa_general_form" enctype="multipart/form-data" data-parsley-validate>
<input type="hidden" value="<?= csrf_hash() ?>" name="<?= csrf_token() ?>" />
<input type="hidden" name="PrimaryKey" id="tpa_General_PrimaryKey" value="<?= isset($tpa['id']) ? $tpa['id'] : '' ?>" />
<input type="hidden" name="tpa_id" id="tpa_id" />
@ -68,11 +68,11 @@
<div class="form-row">
<div class="form-group col-md-4">
<label for="tpa_name">TPA Name<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="tpa_name" placeholder="Enter TPA Name" value="<?= isset($tpa['name']) ? $tpa['name'] : '' ?>" name="name" required>
<input type="text" class="form-control" id="tpa_name" placeholder="Enter TPA Name" value="<?= isset($tpa['name']) ? $tpa['name'] : '' ?>" name="name" required data-parsley-minlength="2">
</div>
<div class="form-group col-md-4">
<label for="tpa_short_name">TPA Short Name<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="short_name" placeholder="Enter Short Name" value="<?= isset($tpa['short_name']) ? $tpa['short_name'] : '' ?>" name="short_name" required>
<input type="text" class="form-control" id="short_name" placeholder="Enter Short Name" value="<?= isset($tpa['short_name']) ? $tpa['short_name'] : '' ?>" name="short_name" required data-parsley-minlength="3">
</div>
<div class="form-group col-md-4">
<label for="tpa_network_hospitals">Network Hospitals<span class="text-danger">*</span></label>
@ -151,12 +151,17 @@
</div>
</div>
</div>
<div id="tpa_logo_error_container"></div>
<!-- Hidden File Input -->
<input type="file" class="hidden-input"
id="tpa_logo" name="tpa_logo"
accept="image/jpeg, image/jpg, image/png"
onchange="PreviewImage0(event);" />
onchange="PreviewImage0(event);"
data-parsley-max-file-size="200"
data-parsley-file-extension="jpg,jpeg,png"
data-parsley-errors-container="#tpa_logo_error_container"
/>
</div>
<!-- TPA Logo Ends -->
<!-- Front Image -->
@ -184,12 +189,17 @@
</div>
</div>
</div>
<div id="fc_error_container"></div>
<!-- Hidden File Input -->
<input type="file" class="hidden-input"
id="fc" name="fc"
accept="image/jpeg, image/jpg, image/png"
onchange="PreviewImage1(event);" />
onchange="PreviewImage1(event);"
data-parsley-max-file-size="500"
data-parsley-file-extension="jpg,jpeg,png"
data-parsley-errors-container="#fc_error_container"
/>
</div>
<!-- Front Image Ends -->
<!-- Back Image -->
@ -217,12 +227,17 @@
</div>
</div>
</div>
<div id="bc_error_container"></div>
<!-- Hidden File Input -->
<input type="file" class="hidden-input"
id="bc" name="bc"
accept="image/jpeg, image/jpg, image/png"
onchange="PreviewImage4(event);" />
onchange="PreviewImage4(event);"
data-parsley-max-file-size="500"
data-parsley-file-extension="jpg,jpeg,png"
data-parsley-errors-container="#bc_error_container"
/>
</div>
<!-- Back Image Ends -->
</div>
@ -264,6 +279,42 @@
<script>
$(document).ready(function() {
// Custom Parsley validator for file size
window.Parsley.addValidator('maxFileSize', {
validateString: function(value, maxSize, parsleyInstance) {
if (!window.FileReader) {
alert('You browser does not support FileReader API, please upgrade.');
return true;
}
var files = parsleyInstance.$element[0].files;
if (files.length == 0) {
return true; // No file, so validation passes (unless required)
}
return files[0].size <= maxSize * 1024;
},
requirementType: 'integer',
messages: {
en: 'This file should not be larger than %s KB.'
}
});
// Custom Parsley validator for file extension
window.Parsley.addValidator('fileExtension', {
validateString: function(value, requirement, parsleyInstance) {
var file = parsleyInstance.$element[0].files[0];
if (!file) {
return true; // No file, so validation passes
}
var extensions = requirement.split(',').map(function(ext) { return ext.trim().toLowerCase(); });
var fileExtension = file.name.split('.').pop().toLowerCase();
return extensions.indexOf(fileExtension) !== -1;
},
requirementType: 'string',
messages: {
en: 'Allowed extensions are: %s.'
}
});
if($('#tpa_General_PrimaryKey').val() != ""){
$('#view_pre_btn').show()

View File

@ -28,6 +28,36 @@
overflow-y: auto;
max-height: 80vh;
}
#tpa-branch-datatable{
width: 100% !important;
}
#tpa-branch-datatable_wrapper .datatable-search-wrapper{
width: auto !important;
}
#tpa-branch-datatable_wrapper .dataTables_filter input{
width: 220px !important;
padding-right: 34px !important;
}
#tpa-branch-datatable_wrapper .datatable-search-icon,
#tpa-branch-datatable_wrapper .datatable-clear-icon{
right: 11px !important;
}
/* Fix DataTables scroll header and body alignment */
#tpa-branch-datatable_wrapper .dataTables_scrollHeadInner{
box-sizing: content-box !important;
padding-right: 0px !important;
width: 100% !important;
}
#tpa-branch-datatable_wrapper .dataTables_scrollHeadInner table,
#tpa-branch-datatable_wrapper .dataTables_scrollBody table{
width: 100% !important;
}
</style>
<div class="tab-pane fade" id="branch-tab">
@ -37,7 +67,7 @@
<div class="table-responsive" id="branch_table" >
<table data-custom-table-css="table" class="table table-borderless table-nowrap mb-0" id="tpa-branch-datatable">
<table data-custom-table-css="table" class="table table-borderless table-nowrap mb-0 w-100 nowrap" id="tpa-branch-datatable">
<thead class="">
<tr>
@ -315,12 +345,56 @@
var contactCount = 1;
function resetTPABranchFormState() {
var $form = $('#tpa_branch_form');
if ($form.length && $form[0] && typeof $form[0].reset === 'function') {
$form[0].reset();
}
if ($form.length && typeof $form.parsley === 'function') {
try {
$form.parsley().reset();
} catch (e) {}
}
contactCount = 1;
$('#container').empty();
$('#contact_1_wrapper input').val('');
$('#tpa_Branch_PrimaryKey').val('');
toggleAddButton();
}
$(document).ready(function () {
$('#tpa-branch-datatable').DataTable({
paging: true ,
searching: true
});
paging: true,
searching: true,
autoWidth: false,
scrollX: false,
responsive: false,
initComplete: function () {
var $wrapper = $('#tpa-branch-datatable_wrapper');
$wrapper.find('.dataTables_scrollHeadInner').css({
'box-sizing': 'content-box',
'padding-right': '0px',
'width': '100%'
});
this.api().columns.adjust();
},
dom: "<'datatable-top-section'f>" +
"tr" +
"<'datatable-bottom-section d-flex justify-content-between align-items-center mt-2'<'datatable-info'i><'datatable-controls d-flex align-items-center'<'me-2'l>p>>",
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:11px; top:50%; transform:translateY(-53%); color:#666;"></i>
<i class="mdi mdi-close-circle datatable-clear-icon" style="position:absolute; right:11px; top:50%; transform:translateY(-53%); color:#666; display:none;"></i>
</div>`,
searchPlaceholder: "Search",
emptyTable: '<div class="text-center text-muted">No data available</div>'
}
});
var tpa_Branch_PrimaryKey = $('#tpa_id_branch').val();
var tpa_branch_form_action = '';
@ -331,7 +405,10 @@ $(document).ready(function () {
loadBranchList();
$('#btnBranchAdd').click(function(){
$('#btnBranchAdd').click(function(e){
if (e && typeof e.preventDefault === 'function') {
e.preventDefault();
}
tpa_branch_form_action = '<?= base_url("master/tpa/branch/create"); ?>';
var myModal = new bootstrap.Modal(document.getElementById('tpaBranchModal'));
@ -375,31 +452,20 @@ $(document).ready(function () {
}
localStorage.removeItem('buttonIds')
}
})
});
$('.btnBack').click(function(){
$('.btnBack').click(function(e){
if (e && typeof e.preventDefault === 'function') {
e.preventDefault();
}
var myModal = new bootstrap.Modal(document.getElementById('tpaBranchModal'));
myModal.hide();
// $('#add_branch').hide();
// $('#branch_table').show();
// $('.btnBack').hide();
// $('#btnBranchAdd').show();
});
$('#branch_name').val('');
$('#branch_code').val('');
$('#state').val('');
$('#district').val('');
$('#branch_city').val('');
$('#name').val('');
$('#email').val('');
$('#mobile').val('');
$('#designation').val('');
$('#container').empty(); $('#contact_1_wrapper input').val('');
})
$('#tpaBranchModal').on('hidden.bs.modal', function () {
resetTPABranchFormState();
});
if(tpa_Branch_PrimaryKey !== ''){
@ -466,7 +532,7 @@ $(document).ready(function () {
}
$('#tpa_branch_list').append(tpaBranchTableInsert);
$('#container').empty(); $('#contact_1_wrapper input').val('');
$('.close').click();
$('#tpaBranchModal .close').trigger('click');
// $('#add_branch').hide();
// $('#branch_table').show();
// $('.btnBack').hide();
@ -509,7 +575,7 @@ $(document).ready(function () {
$('.loader').fadeOut();
$('.loader-mask').delay(350).fadeOut('slow');
}, 1000);
$('.close').click();
$('#tpaBranchModal .close').trigger('click');
}
});
}

View File

@ -49,7 +49,7 @@
<h4 style="position: relative;">TPA</h4>
</div>
<div class="col-6" style="text-align: right;">
<a href="<?= base_url("master/tpa/list"); ?>"><i class="mdi mdi-arrow-left" style="font-size: 17px;"></i> </a>
<a href="<?= base_url("master/tpa/list"); ?>"><i class="ri-arrow-left-circle-line" style="font-size: 17px;"></i> </a>
</div> -->
<!-- </div> -->
<div class="tab-wrapper position-relative">

View File

@ -1,17 +1,4 @@
<style>
.table th,
.table td {
padding: 8px;
}
table.dataTable tbody td {
padding: 4px 4px !important;
}
/* table.dataTable thead th {
padding: 4px 4px !important;
} */
.col-12 {
max-width: 98% !important;
}
@ -22,26 +9,6 @@ table.dataTable tbody td {
background-color: #f0f0f0;
color: #666;
}
.custom-dropdown-menu {
display: none;
position: absolute;
background-color: #ffffff !important;
border: 1px solid rgba(0, 0, 0, 0.15);
border-radius: 0.25rem;
padding: 0.5rem 0;
min-width: 10rem;
z-index: 9999;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.custom-dropdown-menu .dropdown-item {
display: block !important;
width: 100% !important;
padding: 0.5rem 1rem !important;
color: #212529 !important;
text-decoration: none !important;
background-color: transparent !important;
}
.custom-dropdown-menu .dropdown-item:hover {
background-color: #f8f9fa !important;
@ -73,17 +40,17 @@ table.dataTable tbody td {
<h4 style="position: relative;">Vehicle Master</h4>
</div>
</div> -->
<table data-custom-table-css="table" id="scroll-horizontal-datatable" class="table w-100 nowrap">
<table data-custom-table-css="table" id="scroll-horizontal-datatable" class="table mb-0 nowrap" cellspacing="0">
<thead class="bg-light">
<tr>
<th class="font-weight-medium"><div class="column-header">S.No&nbsp;</div></th>
<th class="font-weight-medium"><div class="column-header">Owner Type&nbsp;</div></th>
<th class="font-weight-medium"><div class="column-header">Owner Name&nbsp;</div></th>
<th class="font-weight-medium"><div class="column-header">RC Book No&nbsp;</div></th>
<th class="font-weight-medium"><div class="column-header">Vehicle No&nbsp;</div></th>
<th class="font-weight-medium"><div class="column-header">Vehicle type&nbsp;</div></th>
<th class="font-weight-medium"><div class="column-header">Description&nbsp;</div></th>
<th class="font-weight-medium"><div class="column-header">Action&nbsp;</div></th>
<th class="font-weight-medium">S.No&nbsp;</th>
<th class="font-weight-medium">Owner Type&nbsp;</th>
<th class="font-weight-medium">Owner Name&nbsp;</th>
<th class="font-weight-medium">RC Book No&nbsp;</th>
<th class="font-weight-medium">Vehicle No&nbsp;</th>
<th class="font-weight-medium">Vehicle type&nbsp;</th>
<th class="font-weight-medium">Description&nbsp;</th>
<th class="font-weight-medium text-center">Action&nbsp;</th>
</tr>
</thead>
@ -97,7 +64,7 @@ table.dataTable tbody td {
<td><?php echo $row['vehicle_no'] ?: 'N/A'; ?></td>
<td><?php echo $row['vehicle_type'] ?: 'N/A'; ?></td>
<td><?php echo $vehicle_des[$row['description']] ?? "N/A"; ?></td>
<td>
<td class="text-center table-action-cell">
<div class="btn-group dropdown">
<a href="javascript: void(0);"
class="dropdown-toggle arrow-none btn btn-light btn-sm" data-toggle="dropdown"
@ -135,7 +102,7 @@ table.dataTable tbody td {
aria-hidden="true">×</button>
</div>
<div class="modal-body">
<form class="parsley-examples" method="post" id="vehicle_form" enctype="multipart/form-data">
<form class="parsley-examples" method="post" id="vehicle_form" enctype="multipart/form-data" data-parsley-validate>
<div class="form-group">
<input type="hidden" id="vehicle_primary_key" name="vehicle_primary_key">
@ -155,16 +122,15 @@ table.dataTable tbody td {
<div class="form-group col-md-6">
<label for="cost_center">Vehicle No<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="vehicle_no" placeholder="Enter Vehicle No"
name="vehicle_no"
name="vehicle_no" required
data-parsley-pattern="^[A-Z]{2}[0-9]{1,2}[A-Z]{1,2}[0-9]{4}$"
data-parsley-error-message="Invalid Vehicle Number. Example: TN22AB1234"
data-parsley-trigger="change"
data-parsley-pattern="^[A-Z]{2}[0-9]{2}[A-Z]{1,2}[0-9]{4}$" required>
data-parsley-trigger="keyup">
</div>
<div class="form-group col-md-6">
<label for="rc">RC Book <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="rc" placeholder="Enter RC Book No" name="rc"
required>
<input type="text" class="form-control" id="rc" placeholder="Enter RC Book No" name="rc" required data-parsley-trigger="keyup">
</div>
<div class="form-group col-md-6">
@ -197,8 +163,7 @@ table.dataTable tbody td {
<div class="form-group col-md-6">
<label for="rc"> Old Owner Name <span class="text-danger">(if exist)</span></label>
<input type="text" class="form-control" id="old_onwer" name="old_onwer"
placeholder="Enter Old Owner Name">
<input type="text" class="form-control" id="old_onwer" name="old_onwer" placeholder="Enter Old Owner Name" data-parsley-pattern="^[a-zA-Z0-9\s\.]+$" data-parsley-trigger="keyup">
</div>
<div class="form-group col-md-12">
@ -220,7 +185,7 @@ table.dataTable tbody td {
</div>
<div class="form-group text-right m-b-0" id="hide_smbt_btn">
<button class="btn app-btn-outline-secondary mr-2 colse" type="button" id="close_btn" data-dismiss="modal" aria-hidden="true">Cancel</button>
<button class="btn app-btn-outline-secondary mr-2 close" type="button" data-dismiss="modal" aria-hidden="true">Cancel</button>
<button type="submit" id="btnSubmit" class="btn app-btn-secondary waves-effect waves-light" id="cd_master_btn_Submit">Submit</button>
</div>
</form>
@ -243,7 +208,7 @@ table.dataTable tbody td {
<button type="button" id="close_btn" class="close" data-dismiss="modal" aria-hidden="true">×</button>
</div>
<div class="modal-body">
<form class="parsley-examples" method="post" id="client_form" enctype="multipart/form-data">
<form class="parsley-examples" method="post" id="client_form" enctype="multipart/form-data" data-parsley-validate>
<div class="form-group">
<div class="form-row">
@ -258,16 +223,16 @@ table.dataTable tbody td {
</div>
<div class="form-group col-md-12">
<label for="client_name">Client Name<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="client_name" name="client_name" required>
<input type="text" class="form-control" id="client_name" name="client_name" required data-parsley-pattern="^[a-zA-Z0-9\s\.,\-_'&\(\)]+$" data-parsley-trigger="keyup">
</div>
<div class="form-group col-md-12">
<label for="short_name">Client Short name<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="short_name" name="short_name" required>
<input type="text" class="form-control" id="short_name" name="short_name" required data-parsley-pattern="^[a-zA-Z0-9_\-]+$" data-parsley-trigger="keyup">
</div>
<div class="form-group col-md-12">
<label for="cost_center">PAN<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="pan" placeholder="Enter PAN No" name="pan">
<input type="text" class="form-control" id="pan" placeholder="Enter PAN No" name="pan" required data-parsley-pattern="^[A-Z]{5}[0-9]{4}[A-Z]$" data-parsley-trigger="keyup">
</div>
</div>
@ -278,25 +243,22 @@ table.dataTable tbody td {
<div class="form-group col-md-6">
<label for="dob">Date of Birth<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="dob" placeholder="DD/MM/YYYY" name="dob">
<input type="text" class="form-control" id="dob" placeholder="DD/MM/YYYY" name="dob" data-parsley-pattern="^(0[1-9]|[12][0-9]|3[01])\/(0[1-9]|1[012])\/(19|20)\d\d$" data-parsley-pattern-message="Please enter a valid date in DD/MM/YYYY format.">
</div>
<div class="form-group col-md-6">
<label for="cost_center">Mobile<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="phone" placeholder="Enter Mobile Number"
maxlength="10" name="phone" onkeypress="return onlyNumbers(event)">
<input type="text" class="form-control" id="phone" placeholder="Enter Mobile Number" name="phone" data-parsley-type="digits" data-parsley-length="[10,10]">
</div>
<div class="form-group col-md-12">
<label for="cost_center">Email<span class="text-danger">*</span></label>
<input type="email" class="form-control" id="email" placeholder="Enter Email"
name="email">
<input type="email" class="form-control" id="email" placeholder="Enter Email" name="email">
</div>
<div class="form-group col-md-12">
<label for="aadhar">Aadhar<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="aadhar" placeholder="Enter PAN No"
name="aadhar">
<input type="text" class="form-control" id="aadhar" placeholder="Enter Aadhaar No" name="aadhar" data-parsley-type="digits" data-parsley-length="[12,12]">
</div>
</div>
@ -320,23 +282,22 @@ table.dataTable tbody td {
<div class="form-group col-md-6">
<label for="client_name">Branch Name<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="branch_name" name="branch_name">
<input type="text" class="form-control" id="branch_name" name="branch_name" data-parsley-pattern="^[a-zA-Z0-9\s\.,\-_'&\(\)]+$">
</div>
<div class="form-group col-md-6">
<label for="branch_code">Branch Code<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="branch_code" name="branch_code">
<input type="text" class="form-control" id="branch_code" name="branch_code" data-parsley-pattern="^[a-zA-Z0-9_\-]+$">
</div>
<div class="form-group col-md-6">
<label for="branch_code">Name<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="name" name="name">
<input type="text" class="form-control" id="name" name="name" data-parsley-pattern="^[a-zA-Z\s\.]+$">
</div>
<div class="form-group col-md-6">
<label for="branch_code">Mobile<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="mobile" maxlength="10" name="mobile"
onkeypress="return onlyNumbers(event)">
<input type="text" class="form-control" id="mobile" name="mobile" data-parsley-type="digits" data-parsley-length="[10,10]">
</div>
<div class="form-group col-md-12">
@ -361,98 +322,6 @@ table.dataTable tbody td {
</div>
<!-- end of Client form content modal -->
<script>
document.addEventListener("DOMContentLoaded", function () {
const table = document.getElementById("scroll-horizontal-datatable");
// Create custom dropdown
function createCustomDropdown(row) {
// Get the original dropdown menu
const originalDropdown = row.querySelector('.dropdown-menu');
if (!originalDropdown) return null;
// Create new dropdown menu with the same content
const customDropdown = document.createElement('div');
customDropdown.className = 'custom-dropdown-menu';
// Copy content of the original dropdown
customDropdown.innerHTML = originalDropdown.innerHTML;
return customDropdown;
}
let activeDropdown = null;
// Add click event listener to rows
table.querySelectorAll("tbody tr").forEach(row => {
const customDropdown = createCustomDropdown(row);
if (!customDropdown) return;
// Append custom dropdown to the body
document.body.appendChild(customDropdown);
row.addEventListener("click", function(event) {
// Prevent opening dropdown if clicking on the action column (button)
if (event.target.closest('td:last-child') || event.target.closest('.dropdown-toggle')) {
return;
}
// Close any existing active dropdown
if (activeDropdown) {
activeDropdown.style.display = 'none';
}
// Get the position of the clicked cell
const rect = event.target.getBoundingClientRect();
// Position the dropdown based on where you click in the row (excluding action column)
customDropdown.style.display = 'block';
customDropdown.style.position = 'absolute';
customDropdown.style.left = `${rect.left + window.scrollX}px`; // Align with the clicked position
customDropdown.style.top = `${rect.bottom + window.scrollY + 5}px`; // 5px gap below the row
// Set this dropdown as the active one
activeDropdown = customDropdown;
event.stopPropagation(); // Prevent event from bubbling up
});
// Add click event to handle dropdown item actions
customDropdown.querySelectorAll('.dropdown-item').forEach(item => {
item.addEventListener('click', function(e) {
const onclickAttr = this.getAttribute('onclick');
if (onclickAttr) {
eval(onclickAttr);
}
const href = this.getAttribute('href');
if (href && href !== '#') {
window.location.href = href;
}
// Close the dropdown after handling the click
if (activeDropdown) {
activeDropdown.style.display = 'none';
activeDropdown = null;
}
e.stopPropagation(); // Prevent event from bubbling up
});
});
});
// Close dropdown when clicking outside
document.addEventListener("click", function(event) {
if (activeDropdown && !event.target.closest('.custom-dropdown-menu')) {
activeDropdown.style.display = 'none';
activeDropdown = null;
}
});
});
</script>
<script>
var client_list = ''; // local variable for storing the client list
@ -467,9 +336,9 @@ $(document).ready(function() {
$(document).ready(function() {
var ticketsTable = $('#scroll-horizontal-datatable');
if (ticketsTable.length) {
ticketsTable.DataTable({
var table = $('#scroll-horizontal-datatable');
if (table.length) {
var dataTable = table.DataTable({
scrollX: true,
// dom: "<'row'<'col-12 d-flex justify-content-between align-items-center'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>" +
// "<'row'<'col-sm-12'tr>>" +
@ -533,6 +402,25 @@ $(document).ready(function() {
pageLength: 10,
order: [[0, 'desc']]
});
function applyBottomRowDropup() {
if (!dataTable) return;
const currentRows = dataTable.rows({ page: 'current' }).nodes().toArray();
$('#scroll-horizontal-datatable tbody tr').removeClass('nh-force-dropup');
$('#scroll-horizontal-datatable tbody tr td.table-action-cell .btn-group.dropdown').removeClass('dropup');
const targetCount = Math.min(2, currentRows.length);
for (let i = 0; i < targetCount; i++) {
const row = currentRows[currentRows.length - 1 - i];
if (!row) continue;
$(row).addClass('nh-force-dropup');
$(row).find('td.table-action-cell .btn-group.dropdown').addClass('dropup');
}
}
applyBottomRowDropup();
$('#scroll-horizontal-datatable').on('draw.dt', applyBottomRowDropup);
} else {
console.error("Table atet found.");
}
@ -596,8 +484,13 @@ function getEditVehicleDetailsData(input) {
function showModal(type) {
if (type == 'add') {
$('#vehicle_form')[0].reset();
if ($('#vehicle_form').parsley()) {
$('#vehicle_form').parsley().reset();
}
$('#myCenterModalLabel').text('Add New Vehicle');
$('#owner').removeClass('readonly-select');
$('#owner_branch').removeClass('readonly-select');
$('#Owner_type').val('');
$('#vehicle_no').val('');
$('#rc').val('');
@ -662,10 +555,10 @@ function ownerTypeBasedShowAndHideOwnerBranch(input) {
if (owner_type_id == 2) {
$('.ownerbranchdiv').hide();
// $('#owner_branch').prop('required', false);
$('#owner_branch').prop('required', false);
} else {
$('.ownerbranchdiv').show();
// $('#owner_branch').prop('required', true);
$('#owner_branch').prop('required', true);
}
// Clear and add default options for client

View File

@ -40,13 +40,8 @@
float: left !important;
}
.table th,
.table td {
padding: 8px;
}
table.dataTable tbody td {
padding: 4px 4px !important;
.col-12 {
max-width: 98% !important;
}
/* Tooltip Styles */
@ -316,15 +311,15 @@
<h4 style="position: relative;">Users</h4>
</div>
</div> -->
<table data-custom-table-css="table" class="table table-sm m-0 table-centered dt-responsive nowrap w-100" cellspacing="a" id="user-table">
<table data-custom-table-css="table" class="table mb-0 nowrap" cellspacing="0" id="user-table">
<thead class="bg-light">
<tr>
<th class="font-weight-medium">S.No.</th>
<th class="font-weight-medium"></th>
<th class="font-weight-medium">Mobile No</th>
<th class="font-weight-medium">Role</th>
<th class="font-weight-medium">Status</th>
<th class="font-weight-medium">Action</th>
<th class="font-weight-medium">S.No.&nbsp;</th>
<th class="font-weight-medium">&nbsp;</th>
<th class="font-weight-medium">Mobile No&nbsp;</th>
<th class="font-weight-medium">Role&nbsp;</th>
<th class="font-weight-medium">Status&nbsp;</th>
<th class="font-weight-medium text-center">Action&nbsp;</th>
</tr>
</thead>
<tbody>
@ -339,8 +334,8 @@
<?php if($row->is_active == 1){ echo 'Active'; }else{ echo 'In-Active'; } ?>
</span>
</td>
<td>
<div class="btn-group dropdown" style="position: relative !important;left:0px !important;top:0px !important;">
<td class="text-center table-action-cell">
<div class="btn-group dropdown">
<a href="javascript: void(0);" class="dropdown-toggle arrow-none btn btn-light btn-sm" data-toggle="dropdown" aria-expanded="false"><i class="mdi mdi-dots-horizontal"></i></a>
<div class="dropdown-menu dropdown-menu-right" style="cursor: pointer;">
<a data-toggle="modal" data-target="#con-close-modal" class="dropdown-item btnEdit" data-id="<?php echo $row->id; ?>"><i data-id="<?php echo $row->id; ?>" class="mdi mdi-pencil mr-2 text-muted font-18 vertical-middle btnEdit"></i>Edit</a>
@ -870,7 +865,11 @@
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666; display:none;"></i>
</div>`,
searchPlaceholder: "Search",
emptyTable: '<div class="text-center text-muted">No Data found</div>'
emptyTable: '<div class="text-center text-muted">No Data found</div>',
paginate: {
previous: '◄',
next: '►'
}
},
initComplete: function () {
$(".dt-buttons").prepend(`
@ -895,6 +894,24 @@
}
});
function applyBottomRowDropup() {
if (!table) return;
const currentRows = table.rows({ page: 'current' }).nodes().toArray();
$('#user-table tbody tr').removeClass('nh-force-dropup');
$('#user-table tbody tr td.table-action-cell .btn-group.dropdown').removeClass('dropup');
// Mark last two rows on current page to open action menu upward.
const targetCount = Math.min(2, currentRows.length);
for (let i = 0; i < targetCount; i++) {
const row = currentRows[currentRows.length - 1 - i];
if (!row) continue;
$(row).addClass('nh-force-dropup');
$(row).find('td.table-action-cell .btn-group.dropdown').addClass('dropup');
}
}
applyBottomRowDropup();
$('#user-table').on('draw.dt', applyBottomRowDropup);
$('#team').multiselect({

View File

@ -69,18 +69,22 @@
</div>
<div class="modal-body p-4">
<!-- <form class="parsley-examples" id="vehicleTypeForm" enctype="multipart/form-data"> -->
<form id="vehicleTypeForm" enctype="multipart/form-data">
<form id="vehicleTypeForm" enctype="multipart/form-data" data-parsley-validate>
<input type="hidden" name="pk" id="vehicle_type_id"/>
<div class="form-group">
<div class="form-row">
<div class="form-group col-md-12">
<label for="vehicle_type">Vehicle Type<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="vehicle_type" name="vehicle_type" placeholder="Enter Vehicle Type" required>
<input type="text" class="form-control" id="vehicle_type" name="vehicle_type" placeholder="Enter Vehicle Type"
required
data-parsley-pattern="^[a-zA-Z0-9\s_.-]+$"
data-parsley-pattern-message="Vehicle type may only contain alphanumeric, space, underscore, dot, and dash characters."
data-parsley-trigger="keyup">
</div>
</div>
</div>
<div class="form-group text-right m-b-0">
<button type="button" class="btn app-btn-secondary waves-effect waves-light" id="btnSubmit" onclick="handleSaveEditAndDelete('submit')">Submit</button>
<button type="submit" class="btn app-btn-secondary waves-effect waves-light" id="btnSubmit">Submit</button>
</div>
</form>
</div>
@ -103,7 +107,7 @@
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
buttons: [
{
text: 'Add',
text: '<i class="mdi mdi-plus"></i><span class="btn-custom"> Add </span>',
className: 'btn app-btn-primary mr-2',
action: function(e, dt, node, config) {
openModal()
@ -145,7 +149,10 @@
});
$('.close').click(function(){
resetValues()
resetValues();
if ($('#vehicleTypeForm').parsley()) {
$('#vehicleTypeForm').parsley().reset();
}
})
function openModal(){
@ -252,6 +259,9 @@
function resetValues(){
$('#modalLabel').text('Add Vehicle Type');
$('#vehicleTypeForm')[0].reset();
if ($('#vehicleTypeForm').parsley()) {
$('#vehicleTypeForm').parsley().reset();
}
}
function appendEditData(data){
@ -259,5 +269,13 @@
$('#vehicle_type').val(data[0]['vehicle_type']);
openModal()
}
</script>
$('#vehicleTypeForm').on('submit', function(e) {
e.preventDefault();
var $form = $(this);
$form.parsley().validate();
if ($form.parsley().isValid()) {
handleSaveEditAndDelete('submit');
}
});
</script>

View File

@ -0,0 +1,821 @@
/**
* =============================================================================
* NHance custom.css (listing tables / DataTables)
* =============================================================================
* Purpose:
* Shared look for DataTables-based listing pages: toolbar, footer, sort
* arrows, last-column spacing, card-style rows (.app-table-card +
* tbody.app-table-body), and
* safer row action dropdowns.
*
* Load order:
* Include this file in the layout <head> after app-creative and DataTables
* Bootstrap CSS so these rules can override theme defaults.
*
* Related layout:
* app/Views/layout/header.php <link> to this file
*
* Behaviour notes (why some issues happened):
* - Toolbar: pages that used position:absolute on .dataTables_filter broke the
* flex row; we keep the filter in normal flow (see rules below).
* - Sort icons: theme used fixed vertical offsets; we use stacked triangles
* with top:50% for alignment with header text.
* - Action dropdowns: DataTables scrollX wraps the table in .dataTables_scrollBody
* with overflow:auto. Bootstraps dropdown (Popper) then uses a scroll
* boundary that can mis-place menus (often worse on the last row). Global
* JS in layout/footer.php sets Popper boundary and temporarily relaxes
* overflow while the menu is open see comments there.
* =============================================================================
*/
/* -----------------------------------------------------------------------------
* SECTION: DataTables toolbar (first row inside .dataTables_wrapper)
* Search/filter left, export/buttons right, vertically centered.
* ----------------------------------------------------------------------------- */
div.dataTables_wrapper > div.row:first-of-type {
display: flex !important;
flex-wrap: wrap;
align-items: center !important;
justify-content: space-between !important;
gap: 0.15rem 0.25rem;
/* margin-left: 0 !important; */
/* margin-right: 0 !important; */
/* margin-bottom: 0.25rem !important; */
}
/* Match horizontal padding with Bootstrap columns */
div.dataTables_wrapper > div.row:first-of-type > [class*="col-"] {
padding-left: 4px;
padding-right: 4px;
}
/* Keep filter in document flow (overrides stray position:absolute in old views) */
div.dataTables_wrapper div.dataTables_filter {
position: relative !important;
float: none !important;
text-align: left !important;
margin-bottom: 0.35rem;
}
div.dataTables_wrapper .dt-buttons {
text-align: right !important;
float: none !important;
margin-bottom: 0.35rem;
}
div.dataTables_wrapper .datatable-search,
div.dataTables_wrapper .datatable-buttons {
flex: 0 1 auto;
}
/* Keep top controls on one line for desktop/tablet list pages */
@media (min-width: 768px) {
div.dataTables_wrapper > div.row:first-of-type {
flex-wrap: nowrap !important;
}
/* Legacy view-level CSS sets .dt-buttons.btn-group to grid; normalize */
div.dataTables_wrapper .dt-buttons.btn-group,
div.dataTables_wrapper .datatable-buttons.dt-buttons {
display: inline-flex !important;
flex-wrap: wrap;
align-items: center;
justify-content: flex-end;
gap: 0.4rem;
}
/* Prevent search area from consuming full row width */
div.dataTables_wrapper div.dataTables_filter {
margin-bottom: 0 !important;
}
div.dataTables_wrapper div.dataTables_filter label {
display: inline-flex !important;
align-items: center !important;
margin-bottom: 0 !important;
white-space: nowrap;
}
div.dataTables_wrapper .datatable-search-wrapper {
width: min(320px, 100%) !important;
}
}
/* Custom dom: d-flex justify-content-between (e.g. client_list, tpa_list) */
div.dataTables_wrapper .row .d-flex.justify-content-between {
width: 100%;
align-items: center !important;
}
/* -----------------------------------------------------------------------------
* SECTION: DataTables footer (last row info, length, pagination)
* Showing X to Y left; Show n entries + pager right.
* ----------------------------------------------------------------------------- */
div.dataTables_wrapper > div.row:last-of-type {
display: flex !important;
flex-wrap: wrap;
align-items: center !important;
justify-content: space-between !important;
margin-top: 0.35rem !important;
margin-left: 0 !important;
margin-right: 0 !important;
}
/*
* Default Bootstrap/DataTables bottom row often puts length + paginate in the last
* grid column using floats; wide pagination then overlaps the word entries.
* Flex + gap keeps both visible on all listing pages.
*/
div.dataTables_wrapper > div.row:last-of-type > div[class*="col-"]:last-child:has(.dataTables_paginate),
div.dataTables_wrapper > div.row:last-child > div[class*="col-"]:last-child:has(.dataTables_paginate) {
display: flex !important;
flex-wrap: wrap;
align-items: center !important;
justify-content: flex-end !important;
gap: 0.5rem;
}
div.dataTables_wrapper div.dataTables_info {
padding-top: 0.5rem !important;
color: #333 !important;
font-weight: 500 !important;
}
div.dataTables_wrapper div.dataTables_length {
padding-top: 0 !important;
/* Keep “Show … entries” fully visible when pagination is wide (flex row must not shrink this) */
flex-shrink: 0 !important;
margin-right: 0.65rem !important;
overflow: visible !important;
}
div.dataTables_wrapper div.dataTables_length label {
display: inline-flex !important;
align-items: center !important;
gap: 0.35rem;
margin-bottom: 0 !important;
overflow: visible !important;
/* Views set height: 21px which can clip label text */
height: auto !important;
min-height: 0 !important;
}
/* Length “entries” dropdown — light panel */
div.dataTables_wrapper div.dataTables_length select {
/* background-color: #e0f7fa !important; */
border: 1px solid #b2ebf2 !important;
border-radius: 8px !important;
padding: 0.2rem 1.75rem 0.2rem 0.5rem !important;
font-weight: 500 !important;
color: #333 !important;
}
div.dataTables_wrapper div.dataTables_paginate {
text-align: right !important;
padding-top: 0 !important;
flex-shrink: 1 !important;
min-width: 0 !important;
}
div.dataTables_wrapper .dataTables_paginate ul.pagination {
justify-content: flex-end !important;
align-items: center !important;
flex-wrap: wrap;
margin-bottom: 0 !important;
}
/* Extra gap between “entries” and pager when both sit in a flex row (custom dom: …<'me-2'l>p…) */
div.dataTables_wrapper > div.row:last-of-type .d-flex.align-items-center,
div.dataTables_wrapper > div.row:last-child .d-flex.align-items-center {
gap: 0.5rem;
flex-wrap: wrap;
}
/* Length is often wrapped in .me-2 — that wrapper was shrinking and clipping “entries” */
div.dataTables_wrapper > div.row:last-of-type .d-flex > .me-2,
div.dataTables_wrapper > div.row:last-child .d-flex > .me-2 {
flex-shrink: 0 !important;
}
/* Inactive numbered links */
div.dataTables_wrapper .dataTables_paginate .page-item:not(.active):not(.previous):not(.next) .page-link {
background: transparent !important;
border: none !important;
color: #333 !important;
}
/* Previous / Next — shared box; arrows centered via ::before (absolute + translate) */
div.dataTables_wrapper .dataTables_paginate .page-item.previous,
div.dataTables_wrapper .dataTables_paginate .page-item.next {
display: inline-flex !important;
align-items: center !important;
align-self: center !important;
}
div.dataTables_wrapper .dataTables_paginate .page-item.previous .page-link,
div.dataTables_wrapper .dataTables_paginate .page-item.next .page-link {
border-radius: 13px !important;
border: none !important;
box-shadow: none !important;
outline: none !important;
position: relative !important;
/* Hide DataTables “Previous/Next” text; icon is ::before only */
font-size: 0 !important;
line-height: 0 !important;
color: transparent !important;
overflow: hidden !important;
min-width: 32px !important;
width: 32px !important;
min-height: 2.25rem !important;
padding: 0 !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
}
div.dataTables_wrapper .dataTables_paginate .page-item.previous:not(.disabled) .page-link,
div.dataTables_wrapper .dataTables_paginate .page-item.next:not(.disabled) .page-link {
background-color: #ffffff !important;
cursor: pointer !important;
pointer-events: auto !important;
}
/* First page / last page / single page: Bootstrap + DataTables add .disabled on the <li> */
div.dataTables_wrapper .dataTables_paginate .page-item.previous.disabled .page-link,
div.dataTables_wrapper .dataTables_paginate .page-item.next.disabled .page-link {
background-color: #ffffff !important;
cursor: not-allowed !important;
pointer-events: none !important;
opacity: 1 !important;
}
div.dataTables_wrapper .dataTables_paginate .page-item.previous .page-link::before,
div.dataTables_wrapper .dataTables_paginate .page-item.next .page-link::before {
content: "";
position: absolute !important;
left: 50% !important;
top: 50% !important;
transform: translate(-50%, -50%) !important;
text-indent: 0 !important;
line-height: 1 !important;
font-size: 12px !important;
font-family: inherit !important;
display: block !important;
margin: 0 !important;
}
div.dataTables_wrapper .dataTables_paginate .page-item.previous:not(.disabled) .page-link::before {
content: "◄";
color: #333333;
}
div.dataTables_wrapper .dataTables_paginate .page-item.next:not(.disabled) .page-link::before {
content: "►";
color: #333333;
}
div.dataTables_wrapper .dataTables_paginate .page-item.previous.disabled .page-link::before {
content: "◄";
color: #adb5bd !important;
}
div.dataTables_wrapper .dataTables_paginate .page-item.next.disabled .page-link::before {
content: "►";
color: #adb5bd !important;
}
/* DataTables markup variant without .page-item/.page-link */
div.dataTables_wrapper .dataTables_paginate .paginate_button.previous,
div.dataTables_wrapper .dataTables_paginate .paginate_button.next {
border-radius: 13px !important;
background-color: #F5FFFF !important;
border: none !important;
box-shadow: none !important;
outline: none !important;
font-size: 0 !important;
line-height: 0 !important;
color: transparent !important;
position: relative !important;
overflow: hidden !important;
min-width: 32px !important;
width: 32px !important;
min-height: 2.25rem !important;
padding: 0 !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
}
div.dataTables_wrapper .dataTables_paginate .paginate_button.previous:not(.disabled),
div.dataTables_wrapper .dataTables_paginate .paginate_button.next:not(.disabled) {
background-color: #ffffff !important;
cursor: pointer !important;
}
div.dataTables_wrapper .dataTables_paginate .paginate_button.previous.disabled,
div.dataTables_wrapper .dataTables_paginate .paginate_button.next.disabled {
background-color: #ffffff !important;
cursor: not-allowed !important;
pointer-events: none !important;
opacity: 1 !important;
}
div.dataTables_wrapper .dataTables_paginate .paginate_button.previous::before,
div.dataTables_wrapper .dataTables_paginate .paginate_button.next::before {
position: absolute !important;
left: 50% !important;
top: 50% !important;
transform: translate(-50%, -50%) !important;
line-height: 1 !important;
font-size: 12px !important;
display: block !important;
margin: 0 !important;
}
div.dataTables_wrapper .dataTables_paginate .paginate_button.previous:not(.disabled)::before {
content: "◄";
color: #333333;
}
div.dataTables_wrapper .dataTables_paginate .paginate_button.next:not(.disabled)::before {
content: "►";
color: #333333;
}
div.dataTables_wrapper .dataTables_paginate .paginate_button.previous.disabled::before {
content: "◄";
color: #adb5bd !important;
}
div.dataTables_wrapper .dataTables_paginate .paginate_button.next.disabled::before {
content: "►";
color: #adb5bd !important;
}
/* Bootstrap nested markup: show arrow only on .page-link (avoid duplicate icon) */
div.dataTables_wrapper .dataTables_paginate li.paginate_button.previous::before,
div.dataTables_wrapper .dataTables_paginate li.paginate_button.next::before {
content: none !important;
}
/* -----------------------------------------------------------------------------
* SECTION: Header cells vertical alignment for label + sort icons
* ----------------------------------------------------------------------------- */
table.dataTable thead > tr > th {
vertical-align: middle !important;
}
/* Optional wrapper used in some views — keep label from colliding with sort */
table.dataTable thead th .column-header {
display: inline-block;
vertical-align: middle;
margin-right: 0;
max-width: calc(100% - 40px);
}
/* -----------------------------------------------------------------------------
* SECTION: Last column extra right inset (header sort + row actions)
* Rounded teal header and buttons were flush to the edge; this adds
* breathing room on all listing tables using .dataTable.
* ----------------------------------------------------------------------------- */
table.dataTable thead > tr > th:last-child,
table.dataTable tbody > tr > td:last-child {
padding-right: 1.35rem !important;
}
/*
* Action cells: only when the last column contains a Bootstrap dropdown (avoids
* right-aligning numeric/text-only last columns on other listings).
*/
table.dataTable tbody > tr > td:last-child:has(.dropdown) {
position: relative;
text-align: right;
}
table.dataTable tbody > tr > td:last-child:has(.dropdown) .btn-group.dropdown {
display: inline-flex;
justify-content: flex-end;
}
/* -----------------------------------------------------------------------------
* Action column centered / dropdown (opt-in per row)
* Add class="table-action-cell" on the <td> that wraps .btn-group.dropdown
* (any column position; overrides last-column right-align above).
* ----------------------------------------------------------------------------- */
table.dataTable tbody > tr > td.table-action-cell {
text-align: center !important;
vertical-align: middle !important;
}
table.dataTable tbody > tr > td.table-action-cell .btn-group.dropdown {
display: inline-flex !important;
justify-content: center !important;
float: none !important;
margin-right: 0 !important; /* Prevent global dropdown margin shifting anchor */
}
table.dataTable tbody > tr > td:last-child.table-action-cell:has(.dropdown),
table.dataTable tbody > tr > td.table-action-cell:has(.dropdown) {
text-align: center !important;
}
table.dataTable tbody > tr > td:last-child.table-action-cell:has(.dropdown) .btn-group.dropdown {
justify-content: center !important;
}
/* Dropdown menu stacks above row borders / scroll quirks */
div.dataTables_wrapper .dropdown-menu {
z-index: 1050;
}
/* Tbody row actions: static + optional .dropup from footer.js — menu stays inside .btn-group */
div.dataTables_wrapper table.dataTable tbody .btn-group.dropdown {
position: relative !important;
}
div.dataTables_wrapper table.dataTable tbody .btn-group.dropdown .dropdown-menu {
z-index: 1060;
}
/* If global boundary=window ever added .position-static to a row dropdown parent, the menu
loses its anchor; tbody row toggles use data-boundary="scrollParent" this is a safety net. */
div.dataTables_wrapper table.dataTable tbody .btn-group.dropdown.position-static {
position: relative !important;
}
/* -----------------------------------------------------------------------------
* SECTION: Sort arrows stacked triangles (neutral / asc / desc)
* ----------------------------------------------------------------------------- */
table.dataTable thead > tr > th.sorting,
table.dataTable thead > tr > th.sorting_asc,
table.dataTable thead > tr > th.sorting_desc,
table.dataTable thead > tr > th.sorting_asc_disabled,
table.dataTable thead > tr > th.sorting_desc_disabled {
position: relative !important;
/* Room for stacked sort triangles + gap before cell edge */
padding-right: 2.35rem !important;
}
/* Reset theme font icons to CSS triangles */
table.dataTable thead .sorting:before,
table.dataTable thead .sorting_asc:before,
table.dataTable thead .sorting_desc:before,
table.dataTable thead .sorting_asc_disabled:before,
table.dataTable thead .sorting_desc_disabled:before,
table.dataTable thead .sorting:after,
table.dataTable thead .sorting_asc:after,
table.dataTable thead .sorting_desc:after,
table.dataTable thead .sorting_asc_disabled:after,
table.dataTable thead .sorting_desc_disabled:after {
font-family: inherit !important;
font-size: 0 !important;
line-height: 0 !important;
width: 0 !important;
height: 0 !important;
border: none !important;
bottom: auto !important;
opacity: 1 !important;
}
/* Up triangle */
table.dataTable thead .sorting:before,
table.dataTable thead .sorting_asc:before,
table.dataTable thead .sorting_desc:before,
table.dataTable thead .sorting_asc_disabled:before,
table.dataTable thead .sorting_desc_disabled:before {
content: "" !important;
position: absolute;
right: 0.85rem !important;
left: auto !important;
top: 50% !important;
margin-top: -7px !important;
border-left: 4px solid transparent !important;
border-right: 4px solid transparent !important;
border-bottom: 5px solid rgba(178, 235, 242, 0.95) !important;
}
/* Down triangle */
table.dataTable thead .sorting:after,
table.dataTable thead .sorting_asc:after,
table.dataTable thead .sorting_desc:after,
table.dataTable thead .sorting_asc_disabled:after,
table.dataTable thead .sorting_desc_disabled:after {
content: "" !important;
position: absolute;
right: 0.85rem !important;
left: auto !important;
top: 50% !important;
margin-top: 0 !important;
border-left: 4px solid transparent !important;
border-right: 4px solid transparent !important;
border-top: 5px solid rgba(178, 235, 242, 0.95) !important;
}
/* Active: ascending — strong up arrow */
table.dataTable thead .sorting_asc:before {
border-bottom-color: #ffffff !important;
}
table.dataTable thead .sorting_asc:after {
border-top-color: rgba(255, 255, 255, 0.35) !important;
}
/* Active: descending — strong down arrow */
table.dataTable thead .sorting_desc:before {
border-bottom-color: rgba(255, 255, 255, 0.35) !important;
}
table.dataTable thead .sorting_desc:after {
border-top-color: #ffffff !important;
}
/* Sortable but not active */
table.dataTable thead .sorting:before,
table.dataTable thead .sorting:after {
border-bottom-color: rgba(178, 235, 242, 0.95) !important;
border-top-color: rgba(178, 235, 242, 0.95) !important;
}
/* Compact table variant */
table.dataTable.table-sm > thead > tr > th.sorting:before,
table.dataTable.table-sm > thead > tr > th.sorting_asc:before,
table.dataTable.table-sm > thead > tr > th.sorting_desc:before {
margin-top: -7px !important;
}
table.dataTable.table-sm > thead > tr > th.sorting:after,
table.dataTable.table-sm > thead > tr > th.sorting_asc:after,
table.dataTable.table-sm > thead > tr > th.sorting_desc:after {
margin-top: 0 !important;
}
/*
* Last header column only: sort triangles sit 2px from the inner right edge of
* the <th> (all listing pages). Matches product ask: fixed 2px gap vs. rounded edge.
*/
table.dataTable thead > tr > th:last-child.sorting:before,
table.dataTable thead > tr > th:last-child.sorting_asc:before,
table.dataTable thead > tr > th:last-child.sorting_desc:before,
table.dataTable thead > tr > th:last-child.sorting_asc_disabled:before,
table.dataTable thead > tr > th:last-child.sorting_desc_disabled:before,
table.dataTable thead > tr > th:last-child.sorting:after,
table.dataTable thead > tr > th:last-child.sorting_asc:after,
table.dataTable thead > tr > th:last-child.sorting_desc:after,
table.dataTable thead > tr > th:last-child.sorting_asc_disabled:after,
table.dataTable thead > tr > th:last-child.sorting_desc_disabled:after {
right: 0.65rem !important;
}
/* -----------------------------------------------------------------------------
* SECTION: Compact spacing mode for all listing tables
* Reduce extra white space around search/buttons/header/rows.
* ----------------------------------------------------------------------------- */
/* Search input compact height */
div.dataTables_wrapper .dataTables_filter input[type="search"] {
height: 34px !important;
min-height: 34px !important;
padding-top: 0.28rem !important;
padding-bottom: 0.28rem !important;
}
/* DataTables buttons compact */
div.dataTables_wrapper .dt-buttons .btn {
padding-top: 0.34rem !important;
padding-bottom: 0.34rem !important;
}
/* -----------------------------------------------------------------------------
* SECTION: Card-style listing rows
* Markup: <table class="app-table-card" data-custom-table-css="table">
* <tbody class="app-table-body"></tbody>
*
* Why .app-table-card (not :has()):
* - :has() is unsupported in some environments; when invalid, the whole rule
* is dropped, so row gaps never apply.
* - dataTables.bootstrap4 sets table.dataTable { border-spacing: 0 }; we need
* a selector at least as specific as table.table.dataTable with !important.
* ----------------------------------------------------------------------------- */
table[data-custom-table-css="table"].app-table-card.table.dataTable,
table[data-custom-table-css="table"].app-table-card.table,
table[data-custom-table-css="second-table"].app-table-card.table.dataTable,
table[data-custom-table-css="second-table"].app-table-card.table {
border-collapse: separate !important;
border-spacing: 0 6px !important;
}
table[data-custom-table-css="table"].app-table-card thead tr th:first-child,
table[data-custom-table-css="second-table"].app-table-card thead tr th:first-child {
border-top-left-radius: 12px !important;
border-bottom-left-radius: 12px !important;
}
table[data-custom-table-css="table"].app-table-card thead tr th:last-child,
table[data-custom-table-css="second-table"].app-table-card thead tr th:last-child {
border-top-right-radius: 12px !important;
border-bottom-right-radius: 12px !important;
}
table[data-custom-table-css="table"].app-table-card tbody.app-table-body > tr > td,
table[data-custom-table-css="second-table"].app-table-card tbody.app-table-body > tr > td {
background-color: #ffffff !important;
border-style: solid !important;
border-color: #e8ecef !important;
border-width: 1px 0 !important;
vertical-align: middle !important;
transition: background-color 0.2s ease, border-color 0.2s ease;
}
table[data-custom-table-css="table"].app-table-card tbody.app-table-body > tr > td:first-child,
table[data-custom-table-css="second-table"].app-table-card tbody.app-table-body > tr > td:first-child {
border-left-width: 1px !important;
border-top-left-radius: 12px !important;
border-bottom-left-radius: 12px !important;
}
table[data-custom-table-css="table"].app-table-card tbody.app-table-body > tr > td:last-child,
table[data-custom-table-css="second-table"].app-table-card tbody.app-table-body > tr > td:last-child {
border-right-width: 1px !important;
border-top-right-radius: 12px !important;
border-bottom-right-radius: 12px !important;
}
table[data-custom-table-css="table"].app-table-card tbody.app-table-body > tr:hover > td,
table[data-custom-table-css="second-table"].app-table-card tbody.app-table-body > tr:hover > td {
background-color: #f0fafb !important;
border-color: #c5e6e8 !important;
}
/* Reduce header/body padding globally for listing tables */
table[data-custom-table-css="table"].dataTable thead th,
table[data-custom-table-css="second-table"].dataTable thead th {
padding-top: 10px !important;
padding-bottom: 10px !important;
/* Match body cell horizontal inset so header labels line up with td text */
padding-left: 0.75rem !important;
padding-right: 0.75rem !important;
}
table[data-custom-table-css="table"] tbody td,
table[data-custom-table-css="second-table"] tbody td {
padding-top: 8px !important;
padding-bottom: 8px !important;
padding-left: 0.75rem !important;
padding-right: 0.75rem !important;
}
/* Keep listing text in single line (requested no-wrap). */
div.dataTables_wrapper table.dataTable th,
div.dataTables_wrapper table.dataTable td {
white-space: nowrap !important;
}
/* Keep horizontal + vertical scrolling behavior consistent across list pages. */
div.dataTables_wrapper .dataTables_scrollHead,
div.dataTables_wrapper .dataTables_scrollBody {
width: 100% !important;
}
div.dataTables_wrapper .dataTables_scrollBody {
overflow-x: auto !important;
overflow-y: auto !important;
}
/*
* Bootstrap `.table-responsive` uses overflow-x: auto. Listing pages wrap the whole
* `.dataTables_wrapper` (toolbar + scrollX area + pagination) in it, so the same div
* gets a second horizontal scrollbar under the footer. Only `.dataTables_scrollBody`
* should scroll horizontally turn off outer overflow when a DT wrapper is inside.
* Class is set from layout/footer.php on `init.dt`; `:has()` covers edge cases without JS.
*/
.table-responsive.nh-dt-no-outer-scroll,
.table-responsive:has(.dataTables_wrapper) {
overflow-x: visible !important;
-webkit-overflow-scrolling: auto;
}
div.dataTables_wrapper {
overflow-x: visible !important;
max-width: 100%;
}
/* Let the middle row shrink inside flex layouts so the wrapper doesnt widen the page */
div.dataTables_wrapper > .row .dataTables_scroll {
max-width: 100%;
min-width: 0;
}
/* Reusable listing-table dropdown + footer spacing helpers */
div.dataTables_wrapper .dataTables_scroll {
margin-bottom: 10px;
}
div.dataTables_wrapper > .row:last-child {
clear: both;
position: relative;
}
div.dataTables_wrapper table.dataTable tbody tr.nh-force-dropup td.table-action-cell .btn-group.dropdown .dropdown-menu {
top: auto !important;
bottom: 100% !important;
margin-top: 0 !important;
margin-bottom: 0.15rem !important;
}
div.dataTables_wrapper table.dataTable tbody td.table-action-cell .dropdown-menu {
background-color: #ffffff !important;
opacity: 1 !important;
border: 1px solid rgba(0, 0, 0, 0.12) !important;
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.12) !important;
z-index: 1080 !important;
}
div.dataTables_wrapper table.dataTable tbody td.table-action-cell .dropdown-menu .dropdown-item {
background-color: #ffffff !important;
}
div.dataTables_wrapper table.dataTable tbody td.table-action-cell .dropdown-menu .dropdown-item:hover {
background-color: #f8f9fa !important;
}
/* Row action menu: reusable click-to-open menu for list pages */
table.nh-row-action-table tbody td.table-action-cell .action-menu-toggle {
border: none !important;
background: transparent !important;
padding: 2px 8px !important;
color: #7a7a7a !important;
box-shadow: none !important;
}
table.nh-row-action-table tbody td.table-action-cell .action-menu-toggle:hover,
table.nh-row-action-table tbody td.table-action-cell .action-menu-toggle:focus {
background: #f3f6f9 !important;
color: #495057 !important;
}
table.nh-row-action-table tbody td.table-action-cell .action-menu-toggle i {
font-size: 18px;
line-height: 1;
}
table.nh-row-action-table tbody .nh-row-action-menu {
position: fixed !important;
min-width: 190px;
display: none;
z-index: 1085 !important;
transform: none !important;
}
table.nh-row-action-table tbody .nh-row-action-menu.show {
display: block !important;
bottom: auto !important;
}
/* Force downward opening for custom row-action menu, even if legacy dropup classes exist */
div.dataTables_wrapper table.nh-row-action-table tbody tr.nh-force-dropup td.table-action-cell .btn-group.dropdown .nh-row-action-menu,
div.dataTables_wrapper table.nh-row-action-table tbody td.table-action-cell .btn-group.dropdown.dropup .nh-row-action-menu,
div.dataTables_wrapper table.nh-row-action-table tbody td.table-action-cell .dropup .nh-row-action-menu {
bottom: auto !important;
margin-bottom: 0 !important;
}
/* -----------------------------------------------------------------------------
* SECTION: Small screens stack/center DataTables controls
* ----------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
div.dataTables_wrapper div.dataTables_filter,
div.dataTables_wrapper div.dataTables_length,
div.dataTables_wrapper div.dataTables_info,
div.dataTables_wrapper div.dataTables_paginate {
text-align: center !important;
}
div.dataTables_wrapper .dataTables_paginate ul.pagination {
justify-content: center !important;
}
}
/* ============================================================================
* Last column sort: enough right padding so triangles are not flush to edge
* (still respects th:last-child extra inset from last column section above).
* ============================================================================ */
table.dataTable thead > tr > th.sorting:last-child,
table.dataTable thead > tr > th.sorting_asc:last-child,
table.dataTable thead > tr > th.sorting_desc:last-child,
table.dataTable thead > tr > th.sorting_asc_disabled:last-child,
table.dataTable thead > tr > th.sorting_desc_disabled:last-child {
padding-right: 2.35rem !important;
}
table.dataTable thead > tr > th.sorting:last-child:before,
table.dataTable thead > tr > th.sorting_asc:last-child:before,
table.dataTable thead > tr > th.sorting_desc:last-child:before,
table.dataTable thead > tr > th.sorting_asc_disabled:last-child:before,
table.dataTable thead > tr > th.sorting_desc_disabled:last-child:before,
table.dataTable thead > tr > th.sorting:last-child:after,
table.dataTable thead > tr > th.sorting_asc:last-child:after,
table.dataTable thead > tr > th.sorting_desc:last-child:after,
table.dataTable thead > tr > th.sorting_asc_disabled:last-child:after,
table.dataTable thead > tr > th.sorting_desc_disabled:last-child:after {
right: 0.65rem !important;
}