/** * App Invoice List (js) */ 'use strict'; document.addEventListener('DOMContentLoaded', function () { const dt_invoice_table = document.querySelector('.invoice-list-table'); if (dt_invoice_table) { const dt_invoice = new DataTable(dt_invoice_table, { ajax: assetsPath + 'json/invoice-list.json', columns: [ { data: 'invoice_id' }, { data: 'invoice_id', orderable: false, render: DataTable.render.select() }, { data: 'invoice_id' }, { data: 'invoice_status' }, { data: 'issued_date' }, { data: 'client_name' }, { data: 'total' }, { data: 'balance' }, { data: 'invoice_status' }, { data: 'action' } ], columnDefs: [ { className: 'control', responsivePriority: 2, searchable: false, targets: 0, render: function () { return ''; } }, { targets: 1, orderable: false, searchable: false, responsivePriority: 4, render: function () { return ''; } }, { targets: 2, render: function (data, type, full) { return `#${full['invoice_id']}`; } }, { // Invoice Status with tooltip targets: 3, render: function (data, type, full) { const invoiceStatus = full['invoice_status']; const balance = full['balance']; const dueDate = full['due_date']; const roleBadgeObj = { Sent: '', Draft: '', 'Past Due': '', 'Partial Payment': '', Paid: '', Downloaded: '' }; // Sanitize tooltip content by escaping double quotes const tooltipContent = ` ${invoiceStatus}
Balance: ${balance}
Due Date: ${dueDate} `.replace(/"/g, '"'); return ` ${roleBadgeObj[invoiceStatus] || ''} `; } }, { targets: 4, responsivePriority: 2, render: function (data, type, full) { const name = full['client_name']; const service = full['service']; const image = full['avatar_image']; const randNum = Math.floor(Math.random() * 11) + 1; const userImg = `${randNum}.png`; let output; if (image === true) { output = `Avatar`; } else { const stateNum = Math.floor(Math.random() * 6); const states = ['success', 'danger', 'warning', 'info', 'dark', 'primary', 'secondary']; const state = states[stateNum]; const initials = (name.match(/\b\w/g) || []) .slice(0, 2) .map(letter => letter.toUpperCase()) .join(''); output = `${initials}`; } return `
${output}
${name} ${service}
`; } }, { targets: 5, render: function (data, type, full) { const total = full['total']; return `${total}$${total}`; } }, { targets: 6, render: function (data, type, full) { const dueDate = new Date(full['due_date']); return ` ${dueDate.toISOString().slice(0, 10).replace(/-/g, '')} ${dueDate.toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' })} `; } }, { targets: 7, orderable: false, render: function (data, type, full) { const balance = full['balance']; if (balance === 0) { return ' Paid '; } else { return `${balance}${balance}`; } } }, { targets: 8, visible: false }, { targets: -1, title: 'Actions', searchable: false, orderable: false, render: function () { return ( '
' + '' + '' + '' ); } } ], select: { style: 'multi', selector: 'td:nth-child(2)' }, order: [[2, 'desc']], displayLength: 10, layout: { topStart: { rowClass: 'row m-3 my-0 justify-content-between', features: [ { pageLength: { menu: [10, 25, 50, 100], text: 'Show_MENU_' }, buttons: [ { text: 'Create Invoice', className: 'btn btn-primary', action: function () { window.location = 'app-invoice-add.html'; } } ] } ] }, topEnd: { rowClass: 'row m-3 my-0 justify-content-between', features: [ { search: { placeholder: 'Search Invoice', text: '_INPUT_' } } ] }, bottomStart: { rowClass: 'row mx-3 justify-content-between', features: ['info'] }, bottomEnd: 'paging' }, language: { paginate: { next: '', previous: '', first: '', last: '' } }, responsive: { details: { display: DataTable.Responsive.display.modal({ header: function (row) { const data = row.data(); return 'Details of ' + data['client_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 () { // Ensure the container for the Invoice Status filter is created let invoiceStatusContainer = document.querySelector('.invoice_status'); if (!invoiceStatusContainer) { // Create the container if it doesn't exist invoiceStatusContainer = document.createElement('div'); invoiceStatusContainer.className = 'invoice_status'; // Append it to a suitable location in your DataTable's layout // Example: Appending to the filter area (adjust as needed) const filterArea = document.querySelector('.dt-layout-end'); if (filterArea) { filterArea.appendChild(invoiceStatusContainer); } } // Adding role filter once the table is initialized this.api() .columns(8) .every(function () { const column = this; // Create the dropdown for "Invoice Status" const select = document.createElement('select'); select.id = 'UserRole'; select.className = 'form-select'; select.innerHTML = ''; // Append the dropdown to the invoice status container invoiceStatusContainer.appendChild(select); // Add change event listener to filter the column based on selected value select.addEventListener('change', function () { const val = select.value ? `^${select.value}$` : ''; column.search(val, true, false).draw(); }); // Populate the dropdown with unique values from the column data column .data() .unique() .sort() .each(function (d) { const option = document.createElement('option'); option.value = d; option.className = 'text-capitalize'; option.textContent = d; select.appendChild(option); }); }); } }); function deleteRecord(event) { let row = document.querySelector('.dtr-expanded'); if (event) { row = event.target.parentElement.closest('tr'); } if (row) { dt_invoice.row(row).remove().draw(); } } function bindDeleteEvent() { const invoiceTable = document.querySelector('.invoice-list-table'); const modal = document.querySelector('.dtr-bs-modal'); if (invoiceTable && invoiceTable.classList.contains('collapsed')) { if (modal) { modal.addEventListener('click', function (event) { if (event.target.parentElement.classList.contains('delete-record')) { deleteRecord(); const closeButton = modal.querySelector('.btn-close'); if (closeButton) closeButton.click(); // Simulates a click on the close button } }); } } else { const tableBody = invoiceTable?.querySelector('tbody'); if (tableBody) { tableBody.addEventListener('click', function (event) { if (event.target.parentElement.classList.contains('delete-record')) { deleteRecord(event); } }); } } } // Initial event binding bindDeleteEvent(); // Re-bind events when modal is shown or hidden document.addEventListener('show.bs.modal', function (event) { if (event.target.classList.contains('dtr-bs-modal')) { bindDeleteEvent(); } }); document.addEventListener('hide.bs.modal', function (event) { if (event.target.classList.contains('dtr-bs-modal')) { bindDeleteEvent(); } }); // Initialize tooltips on each table draw dt_invoice.on('draw', function () { const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]'); tooltipTriggerList.forEach(tooltipTriggerEl => { new bootstrap.Tooltip(tooltipTriggerEl, { boundary: document.body }); }); }); } // Filter form control to default size // ? setTimeout used for multilingual table initialization setTimeout(() => { const elementsToModify = [ { selector: '.dt-buttons .btn', classToRemove: 'btn-secondary' }, { selector: '.dt-buttons', classToRemove: 'btn-secondary' }, { selector: '.dt-buttons.btn-group', classToAdd: 'mb-0' }, { selector: '.dt-search .form-control', classToRemove: 'form-control-sm' }, { selector: '.dt-length .form-select', classToRemove: 'form-select-sm' }, { selector: '.dt-length', classToAdd: 'me-0 mb-md-6 mb-6' }, { selector: '.dt-layout-end', classToRemove: 'justify-content-between ms-auto', classToAdd: 'justify-content-md-between justify-content-center d-flex flex-wrap gap-4 mb-sm-0 mb-4 mt-0' }, { selector: '.dt-layout-start', classToRemove: 'd-md-flex me-auto justify-content-between', classToAdd: 'col-12 col-md-6 d-flex justify-content-center justify-content-md-start gap-2' }, { 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); });