ContactBridge

Log | Files | Refs | README

ajaxtable.js (7805B)


      1 class AjaxTable {
      2     constructor(apiEndpoint, id_prefix, tableHeadersId, tableBodyId, searchBarId, pageSizeSelectorId, paginationContainerId) {
      3         this.apiEndpoint = apiEndpoint;
      4         this.tableHeadersId = tableHeadersId;
      5         this.tableBodyId = tableBodyId;
      6         this.id_prefix = id_prefix;
      7         this.searchBarId = searchBarId;
      8         this.pageSizeSelectorId = pageSizeSelectorId;
      9         this.paginationContainerId = paginationContainerId;
     10 
     11         this.currentPage = 1;
     12         this.currentPageSize = 10; // Default page size
     13         this.currentSearchQuery = '';
     14         this.currentSortField = null;
     15         this.currentSortOrder = null;
     16 
     17         this.initEventListeners();
     18         this.fetchData({ page: 1, page_size: 10 });
     19     }
     20 
     21     initEventListeners() {
     22         const debouncedSearchData = this.debounce(this.searchData.bind(this), 300);
     23         document.getElementById(this.searchBarId).addEventListener('keyup', debouncedSearchData);
     24 
     25         document.getElementById(this.pageSizeSelectorId).addEventListener('change', () => {
     26             this.currentPageSize = document.getElementById(this.pageSizeSelectorId).value;
     27             this.currentPage = 1; // Reset to first page on page size change
     28             this.fetchData();
     29         });
     30 
     31         document.addEventListener('htmx:afterRequest', (evt) => {
     32             this.fetchData();
     33         });
     34 
     35         document.getElementById(this.tableBodyId).addEventListener('click', event => {
     36             if (event.target.classList.contains('delete-button')) {
     37                 const itemId = event.target.dataset.itemId;
     38                 this.confirmDelete(this.apiEndpoint, itemId);
     39             }
     40         });
     41 
     42         this.sortTableByHeader();
     43     }
     44 
     45     confirmDelete(apiEndpoint, itemId) {
     46         var instance = M.Modal.getInstance(document.getElementById(this.id_prefix + 'deleteConfirmationModal'));
     47 
     48         // Set up the delete confirmation button's event listener
     49         var confirmButton = document.getElementById(this.id_prefix + 'confirmDeleteButton');
     50         confirmButton.onclick = () => {
     51             this.performDeletion(apiEndpoint, itemId);
     52             instance.close();
     53         };
     54 
     55         // Open the modal
     56         instance.open();
     57     }
     58 
     59     performDeletion(apiEndpoint, itemId) {
     60         const deleteUrl = `${apiEndpoint}${itemId}`;
     61 
     62         fetch(deleteUrl, { method: 'DELETE' })
     63             .then(response => {
     64                 if(response.ok) {
     65                     this.handleDeletionSuccess(itemId);
     66                 } else {
     67                     console.error('Deletion failed');
     68                 }
     69             })
     70             .catch(error => console.error('Error:', error));
     71     }
     72 
     73     handleDeletionSuccess(itemId) {
     74         const rowToRemove = document.getElementById('itemRow-' + itemId);
     75         if (rowToRemove) {
     76             rowToRemove.remove();
     77         } else {
     78             this.fetchData(); // Refresh the table data
     79         }
     80     }
     81 
     82     // Function to fetch data and update the table
     83     fetchData() {
     84         const queryParams = {
     85             page: this.currentPage,
     86             page_size: this.currentPageSize,
     87             query: this.currentSearchQuery
     88         };
     89 
     90         if (this.currentSortField && this.currentSortOrder) {
     91             queryParams.sort_by = this.currentSortField;
     92             queryParams.sort_order = this.currentSortOrder;
     93         }
     94 
     95         const url = new URL(this.apiEndpoint, window.location.origin);
     96         Object.keys(queryParams).forEach(key => url.searchParams.append(key, queryParams[key]));
     97 
     98         fetch(url, { headers: { 'Accept': 'text/html' } })
     99             .then(response => {
    100                 if (!response.ok) {
    101                     throw new Error(`HTTP error! status: ${response.status}`);
    102                 }
    103 
    104                 // Extract custom headers before processing the text
    105                 const totalPages = response.headers.get('X-total-pages');
    106                 const currentPage = response.headers.get('X-current-page');
    107                 this.updatePagination(parseInt(totalPages, 10));
    108 
    109                 return response.text();
    110             })
    111             .then(html => {
    112                 const tableBody = document.getElementById(this.tableBodyId);
    113                 tableBody.innerHTML = html;
    114                 htmx.process(tableBody); // Reinitialize htmx for new content
    115             })
    116             .catch(error => {
    117                 console.error('Fetch error:', error);
    118             });
    119     }
    120 
    121     updatePagination(totalPages) {
    122         console.log(totalPages)
    123         const container = document.getElementById(this.paginationContainerId);
    124         container.innerHTML = '';
    125 
    126         // Create back button
    127         const createBackButton = () => {
    128             const li = document.createElement('li');
    129             li.className = this.currentPage === 1 ? 'disabled' : 'waves-effect';
    130             const a = document.createElement('a');
    131             const icon = document.createElement('i');
    132             icon.className = 'material-icons';
    133             icon.textContent = 'chevron_left';
    134             a.appendChild(icon);
    135             a.onclick = () => {
    136                 if (this.currentPage > 1) {
    137                     this.currentPage -= 1;
    138                     this.fetchData({ page: this.currentPage - 1, page_size: this.currentPageSize });
    139                 }
    140             };
    141             li.appendChild(a);
    142             return li;
    143         };
    144         container.appendChild(createBackButton());
    145 
    146         // Create page buttons
    147         for (let i = 1; i <= totalPages; i++) {
    148             const li = document.createElement('li');
    149             li.className = i === this.currentPage ? 'active' : 'waves-effect';
    150             const a = document.createElement('a');
    151             a.textContent = i;
    152             a.onclick = () => {
    153                 this.currentPage = i;
    154                 this.fetchData({ page: i, page_size: this.currentPageSize });
    155             };
    156             li.appendChild(a);
    157             container.appendChild(li);
    158         }
    159 
    160         // Create forward button
    161         const createForwardButton = () => {
    162             const li = document.createElement('li');
    163             li.className = this.currentPage === totalPages ? 'disabled' : 'waves-effect';
    164             const a = document.createElement('a');
    165             const icon = document.createElement('i');
    166             icon.className = 'material-icons';
    167             icon.textContent = 'chevron_right';
    168             a.appendChild(icon);
    169             a.onclick = () => {
    170                 if (this.currentPage < totalPages) {
    171                     this.currentPage += 1;
    172                     this.fetchData({ page: this.currentPage + 1, page_size: this.currentPageSize });
    173                 }
    174             };
    175             li.appendChild(a);
    176             return li;
    177         };
    178         container.appendChild(createForwardButton());
    179     }
    180 
    181     debounce(func, delay) {
    182         let debounceTimer;
    183         return function() {
    184             const context = this;
    185             const args = arguments;
    186             clearTimeout(debounceTimer);
    187             debounceTimer = setTimeout(() => func.apply(context, args), delay);
    188         };
    189     }
    190 
    191     searchData() {
    192         this.currentSearchQuery = document.getElementById(this.searchBarId).value;
    193         this.currentPage = 1;
    194         this.fetchData();
    195     }
    196 
    197     sortTableByHeader() {
    198         document.querySelectorAll(`#${this.tableHeadersId} th`).forEach(header => {
    199             header.addEventListener('click', () => {
    200                 this.currentSortField = header.getAttribute(this.id_prefix + 'data-field');
    201                 this.currentSortOrder = header.getAttribute(this.id_prefix + 'data-order') === 'asc' ? 'desc' : 'asc';
    202                 header.setAttribute(this.id_prefix + 'data-order', this.currentSortOrder);
    203                 this.fetchData();
    204             });
    205         });
    206     }
    207 }