export class AjaxTable { constructor(options = {}) { this.formId = options.formId || 'filterForm'; this.tableContainerSelector = options.tableContainerSelector || '#userTableContainer'; this.spinnerId = options.spinnerId || 'loadingSpinner'; this.searchInputId = options.searchInputId || 'searchInput'; this.btnClearSearchId = options.btnClearSearchId || 'btnClearSearch'; this.btnClearFiltersId = options.btnClearFiltersId || 'btnClearFilters'; this.flagSendFilterId = options.flagSendFilterId || 'flagSendFilter'; this.monthFilterId = options.monthFilterId || 'monthFilter'; this.skeletonColumns = options.skeletonColumns || 7; this.isLoading = false; this.searchDebounceTimer = null; this.init(); } init() { this.form = document.getElementById(this.formId); this.spinner = document.getElementById(this.spinnerId); this.searchInput = document.getElementById(this.searchInputId); this.btnClearSearch = document.getElementById(this.btnClearSearchId); this.btnClearFilters = document.getElementById(this.btnClearFiltersId); this.flagSendFilter = document.getElementById(this.flagSendFilterId); this.monthFilter = document.getElementById(this.monthFilterId); if (this.form) { this.form.addEventListener('submit', (e) => { e.preventDefault(); this.submitForm(); }); } if (this.searchInput) { this.searchInput.addEventListener('input', () => { this.toggleClearSearchBtn(); clearTimeout(this.searchDebounceTimer); this.searchDebounceTimer = setTimeout(() => { this.submitForm(); }, 500); }); } if (this.btnClearSearch) { this.btnClearSearch.addEventListener('click', () => { if (this.searchInput) { this.searchInput.value = ''; this.toggleClearSearchBtn(); this.submitForm(); } }); } if (this.flagSendFilter) { this.flagSendFilter.addEventListener('change', () => { this.submitForm(); }); } if (this.btnClearFilters) { this.btnClearFilters.addEventListener('click', () => { if (this.searchInput) this.searchInput.value = ''; if (this.flagSendFilter) this.flagSendFilter.value = ''; if (this.monthFilter) { this.monthFilter.value = new Date().toISOString().slice(0, 7); } this.toggleClearSearchBtn(); this.submitForm(); }); } // Intercept pagination links document.addEventListener('click', (e) => { if (this.isLoading) { e.preventDefault(); return; } const link = e.target.closest(`${this.tableContainerSelector} nav a`); if (link) { e.preventDefault(); this.loadUrl(link.href); } }); // Browser history handling window.addEventListener('popstate', () => { this.loadUrl(window.location.href, false); }); this.toggleClearSearchBtn(); } toggleClearSearchBtn() { if (this.searchInput && this.btnClearSearch) { if (this.searchInput.value.trim() !== '') { this.btnClearSearch.classList.remove('hidden'); } else { this.btnClearSearch.classList.add('hidden'); } } } submitForm() { if (!this.form) return; const url = this.form.action + '?' + new URLSearchParams(new FormData(this.form)).toString(); this.loadUrl(url); } getSkeletonHtml() { let html = ''; for (let i = 0; i < 10; i++) { html += `
`; } return html; } async loadUrl(url, updateHistory = true) { if (this.isLoading) return; this.isLoading = true; if (this.spinner) { this.spinner.classList.remove('hidden'); } let formElements = []; if (this.form) { formElements = this.form.querySelectorAll('input, select, button'); formElements.forEach(el => el.disabled = true); } const tableContainer = document.querySelector(this.tableContainerSelector); const tableBody = tableContainer ? tableContainer.querySelector('tbody') : null; if (tableBody) { tableBody.innerHTML = this.getSkeletonHtml(); } try { const [res] = await Promise.all([ fetch(url, { headers: { 'X-Requested-With': 'XMLHttpRequest' } }), new Promise(resolve => setTimeout(resolve, 200)) // smooth transition ]); if (!res.ok) throw new Error('Network response was not ok'); const contentType = res.headers.get('content-type'); const updateDOM = async () => { if (contentType && contentType.includes('application/json')) { const data = await res.json(); const titleEl = document.getElementById('pageTitle'); if (titleEl && data.title !== undefined) { titleEl.innerHTML = data.title; } const statsEl = document.getElementById('statsGrid'); if (statsEl && data.stats !== undefined) { statsEl.innerHTML = data.stats; } if (tableContainer && data.table !== undefined) { tableContainer.innerHTML = data.table; } } else { const html = await res.text(); const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); const currentTitle = document.getElementById('pageTitle'); const newTitle = doc.getElementById('pageTitle'); if (currentTitle && newTitle) currentTitle.innerHTML = newTitle.innerHTML; const currentGrid = document.getElementById('statsGrid'); const newGrid = doc.getElementById('statsGrid'); if (currentGrid && newGrid) currentGrid.innerHTML = newGrid.innerHTML; const newTable = doc.querySelector(this.tableContainerSelector); if (tableContainer && newTable) tableContainer.innerHTML = newTable.innerHTML; } }; if (document.startViewTransition) { document.startViewTransition(updateDOM); } else { await updateDOM(); } if (updateHistory) { window.history.pushState({}, '', url); } const newTableContainer = document.querySelector(this.tableContainerSelector); if (newTableContainer) { newTableContainer.scrollIntoView({ behavior: 'smooth', block: 'start' }); } } catch (err) { console.error('Error fetching data:', err); window.location.reload(); } finally { this.isLoading = false; if (this.spinner) { this.spinner.classList.add('hidden'); } if (this.form) { formElements.forEach(el => el.disabled = false); } this.toggleClearSearchBtn(); } } }