/** * Page User management */ 'use strict'; // Datatable (js) document.addEventListener('DOMContentLoaded', function (e) { let borderColor, bodyBg, headingColor; borderColor = config.colors.borderColor; bodyBg = config.colors.bodyBg; headingColor = config.colors.headingColor; // Variable declaration for table const dt_user_table = document.querySelector('.datatables-users'), userView = baseUrl + 'app/user/view/account', offCanvasForm = document.getElementById('offcanvasAddUser'); // Select2 initialization var select2 = $('.select2'); if (select2.length) { var $this = select2; $this.wrap('
').select2({ placeholder: 'Select Country', dropdownParent: $this.parent() }); } // ajax setup $.ajaxSetup({ headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') } }); // Users datatable if (dt_user_table) { const dt_user = new DataTable(dt_user_table, { processing: true, serverSide: true, ajax: { url: baseUrl + 'user-list', dataSrc: function (json) { // Ensure recordsTotal and recordsFiltered are numeric and not undefined/null if (typeof json.recordsTotal !== 'number') { json.recordsTotal = 0; } if (typeof json.recordsFiltered !== 'number') { json.recordsFiltered = 0; } // Fallback for empty data to avoid pagination NaN issue json.data = Array.isArray(json.data) ? json.data : []; return json.data; } }, columns: [ // columns according to JSON { data: 'id' }, { data: 'id' }, { data: 'name' }, { data: 'email' }, { data: 'email_verified_at' }, { data: 'action' } ], columnDefs: [ { // For Responsive className: 'control', searchable: false, orderable: false, responsivePriority: 2, targets: 0, render: function (data, type, full, meta) { return ''; } }, { searchable: false, orderable: false, targets: 1, render: function (data, type, full, meta) { return `${full.fake_id}`; } }, { // User full name targets: 2, responsivePriority: 4, render: function (data, type, full, meta) { const { name } = full; // Destructuring to get 'name' from the 'full' object // For Avatar badge const stateNum = Math.floor(Math.random() * 6); const states = ['success', 'danger', 'warning', 'info', 'dark', 'primary', 'secondary']; const state = states[stateNum]; // Extract initials from the name const initials = (name.match(/\b\w/g) || []).shift() + (name.match(/\b\w/g) || []).pop(); const initialsUpper = initials.toUpperCase(); // Create avatar badge using template literals const avatar = `${initialsUpper}`; // Create full output for row using template literals const rowOutput = `
${avatar}
${name}
`; // Return the final output as HTML string return rowOutput; } }, { // User email targets: 3, render: function (data, type, full, meta) { const email = full['email']; return '' + email + ''; } }, { // email verify targets: 4, className: 'text-center', render: function (data, type, full, meta) { const verified = full['email_verified_at']; return `${ verified ? '' : '' }`; } }, { // Actions targets: -1, title: 'Actions', searchable: false, orderable: false, render: function (data, type, full, meta) { return ( '
' + `` + `` + '' + '' + '
' ); } } ], order: [[2, 'desc']], layout: { topStart: { rowClass: 'row m-3 my-0 justify-content-between', features: [ { pageLength: { menu: [7, 10, 20, 50, 70, 100], text: '_MENU_' } } ] }, topEnd: { features: [ { search: { placeholder: 'Search User', text: '_INPUT_' } }, { buttons: [ { extend: 'collection', className: 'btn btn-label-secondary dropdown-toggle', text: 'Export', buttons: [ { extend: 'print', title: 'Users', text: 'Print', className: 'dropdown-item', exportOptions: { columns: [1, 2, 3, 4, 5], // prevent avatar to be print format: { body: function (inner, coldex, rowdex) { if (inner.length <= 0) return inner; // Check if inner is HTML content if (inner.indexOf('<') > -1) { const parser = new DOMParser(); const doc = parser.parseFromString(inner, 'text/html'); // Get all text content let text = ''; // Handle specific elements const userNameElements = doc.querySelectorAll('.user-name'); if (userNameElements.length > 0) { userNameElements.forEach(el => { // Get text from nested structure const nameText = el.querySelector('.fw-medium')?.textContent || el.querySelector('.d-block')?.textContent || el.textContent; text += nameText.trim() + ' '; }); } else { // Get regular text content text = doc.body.textContent || doc.body.innerText; } return text.trim(); } return inner; } } }, customize: function (win) { win.document.body.style.color = config.colors.headingColor; win.document.body.style.borderColor = config.colors.borderColor; win.document.body.style.backgroundColor = config.colors.bodyBg; const table = win.document.body.querySelector('table'); table.classList.add('compact'); table.style.color = 'inherit'; table.style.borderColor = 'inherit'; table.style.backgroundColor = 'inherit'; } }, { extend: 'csv', title: 'Users', text: 'Csv', className: 'dropdown-item', exportOptions: { columns: [1, 2, 3, 4, 5], format: { body: function (inner, coldex, rowdex) { if (inner.length <= 0) return inner; // Parse HTML content const parser = new DOMParser(); const doc = parser.parseFromString(inner, 'text/html'); let text = ''; // Handle user-name elements specifically const userNameElements = doc.querySelectorAll('.user-name'); if (userNameElements.length > 0) { userNameElements.forEach(el => { // Get text from nested structure - try different selectors const nameText = el.querySelector('.fw-medium')?.textContent || el.querySelector('.d-block')?.textContent || el.textContent; text += nameText.trim() + ' '; }); } else { // Handle other elements (status, role, etc) text = doc.body.textContent || doc.body.innerText; } return text.trim(); } } } }, { extend: 'excel', text: 'Excel', className: 'dropdown-item', exportOptions: { columns: [1, 2, 3, 4, 5], format: { body: function (inner, coldex, rowdex) { if (inner.length <= 0) return inner; // Parse HTML content const parser = new DOMParser(); const doc = parser.parseFromString(inner, 'text/html'); let text = ''; // Handle user-name elements specifically const userNameElements = doc.querySelectorAll('.user-name'); if (userNameElements.length > 0) { userNameElements.forEach(el => { // Get text from nested structure - try different selectors const nameText = el.querySelector('.fw-medium')?.textContent || el.querySelector('.d-block')?.textContent || el.textContent; text += nameText.trim() + ' '; }); } else { // Handle other elements (status, role, etc) text = doc.body.textContent || doc.body.innerText; } return text.trim(); } } } }, { extend: 'pdf', title: 'Users', text: 'Pdf', className: 'dropdown-item', exportOptions: { columns: [1, 2, 3, 4, 5], format: { body: function (inner, coldex, rowdex) { if (inner.length <= 0) return inner; // Parse HTML content const parser = new DOMParser(); const doc = parser.parseFromString(inner, 'text/html'); let text = ''; // Handle user-name elements specifically const userNameElements = doc.querySelectorAll('.user-name'); if (userNameElements.length > 0) { userNameElements.forEach(el => { // Get text from nested structure - try different selectors const nameText = el.querySelector('.fw-medium')?.textContent || el.querySelector('.d-block')?.textContent || el.textContent; text += nameText.trim() + ' '; }); } else { // Handle other elements (status, role, etc) text = doc.body.textContent || doc.body.innerText; } return text.trim(); } } } }, { extend: 'copy', title: 'Users', text: 'Copy', className: 'dropdown-item', exportOptions: { columns: [1, 2, 3, 4, 5], format: { body: function (inner, coldex, rowdex) { if (inner.length <= 0) return inner; // Parse HTML content const parser = new DOMParser(); const doc = parser.parseFromString(inner, 'text/html'); let text = ''; // Handle user-name elements specifically const userNameElements = doc.querySelectorAll('.user-name'); if (userNameElements.length > 0) { userNameElements.forEach(el => { // Get text from nested structure - try different selectors const nameText = el.querySelector('.fw-medium')?.textContent || el.querySelector('.d-block')?.textContent || el.textContent; text += nameText.trim() + ' '; }); } else { // Handle other elements (status, role, etc) text = doc.body.textContent || doc.body.innerText; } return text.trim(); } } } } ] }, { text: 'Add New User', className: 'add-new btn btn-primary', attr: { 'data-bs-toggle': 'offcanvas', 'data-bs-target': '#offcanvasAddUser' } } ] } ] }, bottomStart: { rowClass: 'row mx-3 justify-content-between', features: [ { info: { text: 'Showing _START_ to _END_ of _TOTAL_ entries' } } ] }, bottomEnd: 'paging' }, displayLength: 7, language: { paginate: { next: '', previous: '', first: '', last: '' } }, // For responsive popup responsive: { details: { display: DataTable.Responsive.display.modal({ header: function (row) { const data = row.data(); return 'Details of ' + data['name']; } }), type: 'column', renderer: function (api, rowIdx, columns) { const data = columns .map(function (col) { return col.title !== '' // Do not show row in modal popup if title is blank (for check box) ? ` ${col.title}: ${col.data} ` : ''; }) .join(''); if (data) { const div = document.createElement('div'); div.classList.add('table-responsive'); const table = document.createElement('table'); div.appendChild(table); table.classList.add('table'); const tbody = document.createElement('tbody'); tbody.innerHTML = data; table.appendChild(tbody); return div; } return false; } } }, initComplete: function () { // Remove btn-secondary from export buttons document.querySelectorAll('.dt-buttons .btn').forEach(btn => { btn.classList.remove('btn-secondary'); }); } }); // Delete Record document.addEventListener('click', function (e) { if (e.target.closest('.delete-record')) { const deleteBtn = e.target.closest('.delete-record'); const user_id = deleteBtn.dataset.id; const dtrModal = document.querySelector('.dtr-bs-modal.show'); // hide responsive modal in small screen if (dtrModal) { const bsModal = bootstrap.Modal.getInstance(dtrModal); bsModal.hide(); } // sweetalert for confirmation of delete Swal.fire({ title: 'Are you sure?', text: "You won't be able to revert this!", icon: 'warning', showCancelButton: true, confirmButtonText: 'Yes, delete it!', customClass: { confirmButton: 'btn btn-primary me-3', cancelButton: 'btn btn-label-secondary' }, buttonsStyling: false }).then(function (result) { if (result.value) { // delete the data fetch(`${baseUrl}user-list/${user_id}`, { method: 'DELETE', headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'), 'Content-Type': 'application/json' } }) .then(response => { if (response.ok) { dt_user.draw(); // success sweetalert Swal.fire({ icon: 'success', title: 'Deleted!', text: 'The user has been deleted!', customClass: { confirmButton: 'btn btn-success' } }); } else { throw new Error('Delete failed'); } }) .catch(error => { console.log(error); }); } else if (result.dismiss === Swal.DismissReason.cancel) { Swal.fire({ title: 'Cancelled', text: 'The User is not deleted!', icon: 'error', customClass: { confirmButton: 'btn btn-success' } }); } }); } }); // edit record document.addEventListener('click', function (e) { if (e.target.closest('.edit-record')) { const editBtn = e.target.closest('.edit-record'); const user_id = editBtn.dataset.id; const dtrModal = document.querySelector('.dtr-bs-modal.show'); // hide responsive modal in small screen if (dtrModal) { const bsModal = bootstrap.Modal.getInstance(dtrModal); bsModal.hide(); } // changing the title of offcanvas document.getElementById('offcanvasAddUserLabel').innerHTML = 'Edit User'; // get data fetch(`${baseUrl}user-list/${user_id}/edit`) .then(response => response.json()) .then(data => { document.getElementById('user_id').value = data.id; document.getElementById('add-user-fullname').value = data.name; document.getElementById('add-user-email').value = data.email; }); } }); // changing the title const addNewBtn = document.querySelector('.add-new'); if (addNewBtn) { addNewBtn.addEventListener('click', function () { document.getElementById('user_id').value = ''; //resetting input field document.getElementById('offcanvasAddUserLabel').innerHTML = 'Add User'; }); } // Filter form control to default size setTimeout(() => { const elementsToModify = [ { selector: '.dt-buttons .btn', classToRemove: 'btn-secondary' }, { selector: '.dt-search .form-control', classToRemove: 'form-control-sm' }, { selector: '.dt-length .form-select', classToRemove: 'form-select-sm', classToAdd: 'ms-0' }, { selector: '.dt-length', classToAdd: 'mb-md-6 mb-0' }, { selector: '.dt-layout-end', classToRemove: 'justify-content-between', classToAdd: 'd-flex gap-md-4 justify-content-md-between justify-content-center gap-0 flex-wrap mt-0' }, { selector: '.dt-layout-start', classToAdd: 'mt-0' }, { selector: '.dt-buttons', classToAdd: 'd-flex gap-4 mb-md-0 mb-6' }, { selector: '.dt-layout-table', classToRemove: 'row mt-2' }, { selector: '.dt-layout-full', classToRemove: 'col-md col-12', classToAdd: 'table-responsive' } ]; // Delete record elementsToModify.forEach(({ selector, classToRemove, classToAdd }) => { document.querySelectorAll(selector).forEach(element => { if (classToRemove) { classToRemove.split(' ').forEach(className => element.classList.remove(className)); } if (classToAdd) { classToAdd.split(' ').forEach(className => element.classList.add(className)); } }); }); }, 100); } // validating form and updating user's data const addNewUserForm = document.getElementById('addNewUserForm'); // user form validation if (addNewUserForm) { const fv = FormValidation.formValidation(addNewUserForm, { fields: { name: { validators: { notEmpty: { message: 'Please enter fullname' } } }, email: { validators: { notEmpty: { message: 'Please enter your email' }, emailAddress: { message: 'The value is not a valid email address' } } }, userContact: { validators: { notEmpty: { message: 'Please enter your contact' } } }, company: { validators: { notEmpty: { message: 'Please enter your company' } } } }, plugins: { trigger: new FormValidation.plugins.Trigger(), bootstrap5: new FormValidation.plugins.Bootstrap5({ // Use this for enabling/changing valid/invalid class eleValidClass: '', rowSelector: function (field, ele) { // field is the field name & ele is the field element return '.mb-6'; } }), submitButton: new FormValidation.plugins.SubmitButton(), autoFocus: new FormValidation.plugins.AutoFocus() } }).on('core.form.valid', function () { // adding or updating user when form successfully validate const formData = new FormData(addNewUserForm); const formDataObj = {}; // Convert FormData to URL-encoded string formData.forEach((value, key) => { formDataObj[key] = value; }); const searchParams = new URLSearchParams(); for (const [key, value] of Object.entries(formDataObj)) { searchParams.append(key, value); } fetch(`${baseUrl}user-list`, { method: 'POST', headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'), 'Content-Type': 'application/x-www-form-urlencoded' }, body: searchParams.toString() }) .then(response => { if (!response.ok) { throw new Error('Network response was not ok'); } return response.text(); }) .then(status => { // Refresh DataTable dt_user_table && new DataTable(dt_user_table).draw(); // Hide offcanvas const offcanvasInstance = bootstrap.Offcanvas.getInstance(offCanvasForm); offcanvasInstance && offcanvasInstance.hide(); // sweetalert Swal.fire({ icon: 'success', title: `Successfully ${status}!`, text: `User ${status} Successfully.`, customClass: { confirmButton: 'btn btn-success' } }); }) .catch(err => { // Hide offcanvas const offcanvasInstance = bootstrap.Offcanvas.getInstance(offCanvasForm); offcanvasInstance && offcanvasInstance.hide(); Swal.fire({ title: 'Duplicate Entry!', text: 'Your email should be unique.', icon: 'error', customClass: { confirmButton: 'btn btn-success' } }); }); }); // clearing form data when offcanvas hidden offCanvasForm.addEventListener('hidden.bs.offcanvas', function () { fv.resetForm(true); }); } // Phone mask initialization const phoneMaskList = document.querySelectorAll('.phone-mask'); // Phone Number if (phoneMaskList) { phoneMaskList.forEach(function (phoneMask) { phoneMask.addEventListener('input', event => { const cleanValue = event.target.value.replace(/\D/g, ''); phoneMask.value = formatGeneral(cleanValue, { blocks: [3, 3, 4], delimiters: [' ', ' '] }); }); registerCursorTracker({ input: phoneMask, delimiter: ' ' }); }); } });