nhance/app/Views/frontend_content_list.php

853 lines
37 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
helper('datatable_view');
$fec_header_labels = ['S.No.', 'Type', 'Content Section', 'Heading', 'Status', 'Action'];
$fec_col_count = count($fec_header_labels);
$fec_col_max_len = array_fill(0, $fec_col_count, 0);
for ($i = 0; $i < $fec_col_count; $i++) {
$fec_col_max_len[$i] = mb_strlen($fec_header_labels[$i]);
}
$fec_rows = $data['fe_list'] ?? [];
$fec_sl = 0;
foreach ($fec_rows as $row) {
++$fec_sl;
$fec_col_max_len[0] = max($fec_col_max_len[0], mb_strlen((string) $fec_sl));
$fec_col_max_len[1] = max($fec_col_max_len[1], mb_strlen((string) ($row['type'] ?? '')));
$fec_col_max_len[2] = max($fec_col_max_len[2], mb_strlen((string) ($row['content_section'] ?? '')));
$fec_col_max_len[3] = max($fec_col_max_len[3], mb_strlen((string) ($row['heading'] ?? '')));
$fec_st = (isset($row['is_active']) && (int) $row['is_active'] === 1) ? 'Active' : 'In-Active';
$fec_col_max_len[4] = max($fec_col_max_len[4], mb_strlen($fec_st));
$fec_col_max_len[5] = max($fec_col_max_len[5], 4);
}
$fec_col_min_px = [48, 88, 120, 140, 88, 72];
$fec_col_max_px = [64, 200, 360, 480, 120, 88];
$fec_col_width_px = nhance_dt_column_widths_px($fec_header_labels, $fec_col_max_len, $fec_col_min_px, $fec_col_max_px);
?>
<style>
.col-12 {
max-width: 98% !important;
}
.dataTables_length label {height: 21px !important;}
<?php for ($i = 0; $i < $fec_col_count; $i++) : ?>
#frontend-content-list-table_wrapper .dataTables_scrollHead table thead th:nth-child(<?= $i + 1 ?>),
#frontend-content-list-table thead th:nth-child(<?= $i + 1 ?>) {
min-width: <?= (int) $fec_col_width_px[$i] ?>px;
width: <?= (int) $fec_col_width_px[$i] ?>px;
box-sizing: border-box;
vertical-align: middle;
overflow: visible !important;
}
#frontend-content-list-table tbody td:nth-child(<?= $i + 1 ?>) {
width: <?= (int) $fec_col_width_px[$i] ?>px;
max-width: <?= (int) $fec_col_width_px[$i] ?>px;
min-width: <?= (int) $fec_col_width_px[$i] ?>px;
box-sizing: border-box;
vertical-align: middle;
}
<?php endfor; ?>
#frontend-content-list-table tbody td:nth-child(1),
#frontend-content-list-table tbody td:nth-child(2),
#frontend-content-list-table tbody td:nth-child(3),
#frontend-content-list-table tbody td:nth-child(4),
#frontend-content-list-table tbody td:nth-child(5) {
overflow: hidden;
text-overflow: ellipsis;
}
#frontend-content-list-table tbody td:nth-child(6) {
overflow: visible;
text-overflow: clip;
}
#frontend-content-list-table tbody td {
padding: 5px 11px !important;
font-size: 13px;
line-height: 1.25;
}
#frontend-content-list-table thead th {
padding: 5px 2.35rem 5px 11px !important;
font-size: 13px;
line-height: 1.25;
}
</style>
<!-- End ADD and EDIT Page HTML -->
<div class="row" id="List-page">
<div class="col-12">
<div class="card">
<div class="card-body">
<table data-custom-table-css="table" class="table mb-0 nowrap" cellspacing="0" id="frontend-content-list-table">
<thead class="bg-light">
<tr>
<!-- `content` `notes` -->
<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>
<?php if(!empty($data['fe_list'])) { $slno = 1; ?>
<?php foreach($data['fe_list'] as $index => $row) { ?>
<tr>
<td><?= $slno++; ?></td>
<td><?= $row['type']; ?></td>
<td><?= $row['content_section']; ?></td>
<td><?= $row['heading']; ?></td>
<td>
<span class="<?php if($row['is_active'] == 1){ echo 'badge badge-primary'; }else{ echo 'badge badge-danger'; } ?>">
<?php if($row['is_active'] == 1){ echo 'Active'; }else{ echo 'In-Active'; } ?>
</span>
</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">
<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>
<?php if ($row['is_active'] == 1): ?>
<a class="dropdown-item" data-id="<?= $row['id']; ?>" onclick="handleSaveEditAndDelete('remove', this)">
<i class="mdi mdi-delete mr-2 text-muted font-18 vertical-middle"></i>Delete
</a>
<?php endif; ?>
</div>
</div>
</td>
</tr>
<?php } } ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- modal content -->
<div id="con-close-modal" class="modal fade app-font-family" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" style="display: none;" data-backdrop="static">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="modalLabel">Add Front End Content</h4>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
</div>
<div class="modal-body p-4">
<form class="parsley-examples" id="frontEndContentForm" enctype="multipart/form-data">
<input type="hidden" name="fe_id" id="fe_id"/>
<div class="form-group">
<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
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
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
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>
<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" 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" 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>
<div class="form-group text-right m-b-0">
<button type="submit" class="btn app-btn-secondary" id="btnSubmit">Submit</button>
</div>
</form>
</div>
</div>
</div>
</div>
<script>
var table;
let content_editor;
let notes_editor;
$(document).ready(function () {
// var baseUrl = '<?php echo base_url(); ?>';
const editorConfig = {
buttons: [
"undo", "redo", "|",
"paragraph",
"bold", "italic", "strikethrough", "|",
"superscript", "subscript", "|",
"ul", "ol", "|",
"outdent", "indent", "|",
"blockquote", "table", "|",
"align", "fontsize", "|",
"source"
],
controls: {
paragraph: {
list: {
p: "Normal",
h1: "Heading 1",
h2: "Heading 2",
h3: "Heading 3",
h4: "Heading 4"
}
}
},
fontsize: ["8px", "10px", "12px", "14px", "16px", "18px", "20px", "22px", "24px"], // Font sizes in pixels
showPlaceholder: false,
toolbarButtonSize: "small",
toolbarAdaptive: false,
saveHeightInStorage: true,
minHeight: 400,
height: 400, // Optional fixed height
defaultMode: "1", // Start in WYSIWYG mode
enableDragAndDropFileToEditor: true, // Allow file uploads via drag and drop
placeholder: "Start typing here...", // Optional placeholder text
};
// const editorConfig = {
// buttons: [
// "undo", "redo", "|",
// "paragraph",
// "bold", "italic", "strikethrough", "|",
// "superscript", "subscript", "|",
// "ul", "ol", "|",
// "outdent", "indent", "|",
// "blockquote", "table", "|",
// "align", "fontsize", "|",
// "image", "video", "|", // <--- Added image and video here
// "source"
// ],
// // Configuration for handling uploads
// uploader: {
// insertImageAsBase64URI: true, // Simple way to handle images without a backend
// url: 'http://localhost/nhance/frontend_content_files',
// format: 'json',
// prepareData: function (formData) {
// return formData;
// },
// process: function (resp) {
// return {
// files: resp.data.files,
// baseurl: resp.data.baseurl,
// error: resp.error,
// msg: resp.msg
// };
// },
// // This tells Jodit how to build the <img> tag
// defaultHandlerSuccess: function (data) {
// const url = data.baseurl + data.files[0];
// this.selection.insertImage(url);
// },
// error: function (e) {
// console.error("Upload Error: ", e);
// }
// },
// // Configuration for the file browser (optional)
// filebrowser: {
// ajax: {
// url: 'http://localhost/nhance/frontend_content_files',
// data: {
// action: 'files' // Custom parameter for your PHP logic
// }
// },
// // This allows you to delete or rename files directly from the browser popup
// removeButtons: [],
// createNewFolder: false,
// deleteFile: true,
// },
// controls: {
// paragraph: {
// list: {
// p: "Normal",
// h1: "Heading 1",
// h2: "Heading 2",
// h3: "Heading 3",
// h4: "Heading 4"
// }
// }
// },
// fontsize: ["8px", "10px", "12px", "14px", "16px", "18px", "20px", "22px", "24px"],
// showPlaceholder: false,
// toolbarButtonSize: "small",
// toolbarAdaptive: false,
// saveHeightInStorage: true,
// minHeight: 400,
// height: 400,
// defaultMode: "1",
// enableDragAndDropFileToEditor: true,
// 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 = $('#frontend-content-list-table');
nhanceListDataTableBeforeInit();
table = ticketsTable.DataTable(nhanceMergeListDataTableOptions({
// 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
dom: "<'row'<'col-sm-7'f><'col-sm-5 text-right'B>>" + // Filter left, buttons right
"<'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]],
buttons: [
{
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');
}
},
{
extend: 'collection',
text: '<span class=" btn-custom"> Export </span><i class="mdi mdi-menu-down"></i>',
className: 'btn app-btn-secondary ',
buttons: [
{
extend: 'csv',
text: '<i class="mdi mdi-file-delimited " ></i><span class=" btn-custom"> CSV </span>',
title: 'Front End Content',
className: 'app-btn-primary ',
exportOptions: {
columns: ':not(:last-child)'
},
}
]
}
],
language: {
search: `
<div class="datatable-search-wrapper" style="position:relative; display:inline-block;">
_INPUT_
<i class="mdi mdi-magnify datatable-search-icon"
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666;"></i>
<i class="mdi mdi-close-circle datatable-clear-icon"
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>',
paginate: {
previous: '◄',
next: '►'
}
},
paging: true, // Enable pagination
pageLength: 10, // Set default number of rows per page (optional)
// ordering: false,
columnDefs: [
<?php for ($i = 0; $i < $fec_col_count; $i++) : ?>
{ targets: <?= $i ?>, width: '<?= (int) $fec_col_width_px[$i] ?>px' },
<?php endfor; ?>
],
}));
nhanceListDataTableAfterInit();
nhanceListDataTableBindAdjust(table);
function applyBottomRowDropup() {
if (!table) return;
const currentRows = table.rows({ page: 'current' }).nodes().toArray();
$('#frontend-content-list-table tbody tr').removeClass('nh-force-dropup');
$('#frontend-content-list-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();
$('#frontend-content-list-table').on('draw.dt', applyBottomRowDropup);
});
$('.close').click(function(){ resetValues(); })
function openModal(){
var myModal = new bootstrap.Modal(document.getElementById('con-close-modal'));
myModal.show();
}
// old Version
// function handleSaveEditAndDelete(type = 'submit', pk = null){
// let url = '<?= base_url('util/nhanceBranchMaster') ?>';
// let method = "POST";
// let requestData = {};
// $('#modalLabel').text('Add Front End Content');
// requestData.pk = pk;
// if(type == 'submit'){
// $("#frontEndContentForm").find("input, select, textarea").each(function () {
// let name = $(this).attr("name");
// let value = $.trim($(this).val());
// if (name) requestData[name] = value;
// });
// }
// if(type == 'remove'){
// method = "DELETE";
// }else if (type == 'edit'){
// method = "GET"
// }
// console.log("type", type)
// console.log("url", url)
// console.log("method", method)
// console.log("requestData", requestData)
// $('.loader').fadeIn();
// $('.loader-mask').fadeIn();
// // Send AJAX request
// sendAjaxRequestForGlobal(url, method, requestData, function(response) {
// console.log('Data fetched successfully:', response);
// if (response.status) {
// if(type == 'edit'){
// $('#modalLabel').text('Edit Front End Content');
// appendEditData(response.data);
// }else{
// toastr.success(response.message, 'Success');
// }
// } else {
// toastr.warning(response.message, 'Warning');
// }
// $('.loader').fadeOut();
// $('.loader-mask').delay(350).fadeOut('slow');
// }, function(xhr, status, error) {
// console.error('Error fetching data:', error);
// console.error(xhr.responseText);
// $('.loader').fadeOut();
// $('.loader-mask').delay(350).fadeOut('slow');
// });
// }
function handleSaveEditAndDelete(type = 'submit', el = null) {
let form = document.getElementById('frontEndContentForm');
let url = '<?= base_url('frontend_content') ?>';
let method = "POST";
let fe_id = el ? $(el).data('id') : null;
let fe_status = 1;
let formData;
if (type === 'submit') {
// CREATE FORMDATA FROM FORM
formData = new FormData(form);
} else if (type == 'edit') {
method = "GET";
if (fe_id) url += '?fe_id=' + fe_id ;
fe_status = el ? $(el).data('status') : 1;
// $('#btnSubmit').addClass('d-none');
// let name_text = fe_status ? "Edit" : "View";
$('#modalLabel').text('Edit Front End Content');
$('#frontEndContentForm')[0].reset();
} else if (type == 'remove') {
method = "DELETE";
if (fe_id) url += '?fe_id=' + fe_id ;
}
// if (typeof fe_status !== 'undefined' && fe_status == 1) {
// // $('#btnSubmit').addClass('d-none');
// return; // optional: stop further actions
// }else{
// // $('#btnSubmit').removeClass('d-none');
// }
$('.loader, .loader-mask').fadeIn();
$.ajax({
url: url,
type: method,
data: (type === 'submit') ? formData : null,
processData: false,
contentType: false,
dataType: 'json',
success: function (response) {
console.log('Response:', response);
if (response.status === 'success' || response.status === true) {
if (type == 'edit') {
let record = response.data.fe_list[0]; // single record
appendEditData(record);
} else if(type == 'remove'){
toastr.success(response.message, 'Success');
// if(el) $(el).closest('tr').remove();
window.location.reload();
} else if(type == 'submit'){
toastr.success(response.message, 'Success');
var myModal = new bootstrap.Modal(document.getElementById('con-close-modal'));
myModal.hide();
$('#frontEndContentForm')[0].reset();
window.location.reload();
}
} else {
toastr.warning(response.message, 'Warning');
}
$('.loader, .loader-mask').fadeOut();
},
error: function (xhr, status, error) {
$('.loader, .loader-mask').fadeOut();
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');
} else {
toastr.error('An unexpected error occurred. Please try again later.', 'Error');
}
}
});
}
function resetValues(){
$('#modalLabel').text('Add Front End Content');
$('#frontEndContentForm')[0].reset();
if (typeof content_editor !== 'undefined') {
content_editor.value = '';
}
if (typeof notes_editor !== 'undefined') {
notes_editor.value = '';
}
}
function appendEditData(data) {
$('#frontEndContentForm')[0].reset();
$('#fe_id').val(data.id);
if (/video/i.test(data.content_section)) {
$('#content_section').val('Video');
$('#type').val(8);
} else {
$('#type').val(data.type);
$('#content_section').val(data.content_section);
}
$('#heading').val(data.heading);
/**
* --------------------------------------------------------------------------
* FIX & EXPLANATION: Safely Loading HTML into the Editor
* --------------------------------------------------------------------------
* ISSUE:
* Loading HTML directly from an AJAX response into a webpage can be a
* major security risk (XSS). One might think `data.content` is unsafe.
*
* RESOLUTION:
* This operation is SAFE and CORRECT because of the "defense-in-depth"
* strategy we implemented in the `AppContentManagementController`.
*
* 1. The `data.content` and `data.notes` being received here have already
* been validated and sanitized on the server *before* they were ever
* saved to the database.
* 2. The controller's `sanitizeHtml()` function removed all dangerous tags
* (like <script>) and attributes (like `onclick`).
*
* Therefore, the HTML in `data.content` is trusted. We can safely assign it
* directly to the Jodit editor's `.value` property, which will correctly
* render the allowed HTML for the user to edit.
*/
if (content_editor) {
content_editor.value = data.content || '';
}
if (notes_editor) {
notes_editor.value = data.notes || '';
}
openModal();
}
// Simple client-side XSS pattern check to give instant feedback
function hasUnsafeHtml(html) {
if (!html) return false;
const decoded = $('<textarea/>').html(html).text().toLowerCase();
const patterns = [
/<\s*script/i,
/on\w+\s*=/i,
/javascript\s*:/i,
/vbscript\s*:/i,
/data\s*:\s*text\/html/i,
/expression\s*\(/i
];
return patterns.some(p => p.test(decoded));
}
$('#frontEndContentForm').on('submit', function(e) {
e.preventDefault();
const form = this;
// HTML5 built-in validation
if (!form.checkValidity()) {
form.reportValidity(); // shows required/pattern tooltips
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)) {
toastr.warning("Content is required", 'Warning');
return;
}
if (hasUnsafeHtml(content_editor.value)) {
toastr.error("Content contains restricted tags or attributes. Script tags, iframes and event handlers (like onclick, onerror, onmouseover) are not allowed.", "Validation Error");
return;
}
}
// Notes validation
if (typeof notes_editor !== 'undefined') {
if (isEditorEmpty(notes_editor.value)) {
toastr.warning("Notes is required", 'Warning');
return;
}
if (hasUnsafeHtml(notes_editor.value)) {
toastr.error("Notes contains restricted tags or attributes. Script tags, iframes and event handlers (like onclick, onerror, onmouseover) are not allowed.", "Validation Error");
return;
}
}
// If valid, submit via AJAX
handleSaveEditAndDelete('submit');
});
function isEditorEmpty(html) {
if (!html) return true;
// Remove tags, &nbsp;, and trim
const text = html
.replace(/<[^>]*>/g, '')
.replace(/&nbsp;/g, '')
.trim();
return text.length === 0;
}
// $('#content_section').on('keyup blur change', function () {
// let val = $(this).val();
// if (/video/i.test(val)) {
// $(this).val('Video');
// $('#type').val(8);
// }
// });
$('#content_section').on('input blur', function () {
let $this = $(this);
let val = $this.val().trim();
// 1. Handle Empty Input
if (val === '') {
$('#type').val('');
return;
}
// 2. Check for "video" (case insensitive)
if (/video/i.test(val)) {
$('#type').val(8);
if (val !== 'Video') { $this.val('Video'); }
} else {
$('#type').val('');
}
});
// 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>
document.addEventListener("DOMContentLoaded", function() {
const table = document.getElementById("frontend-content-list-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;
}
});
});
</script>
-->