class SmartTable { constructor(table) { this.table = table; this.tbody = table.querySelector("tbody"); this.rows = Array.from(this.tbody.querySelectorAll("tr")); this.currentPage = 1; this.rowsPerPage = 10; this.searchTerm = ""; this.sortIndex = null; this.sortOrder = "asc"; this.init(); } init() { this.createControls(); this.attachSortHandlers(); this.render(); } createControls() { const wrapper = this.table.parentNode; // Table controls (entries + search) this.controls = document.createElement("div"); this.controls.className = "table-controls"; // Entries select this.entriesSelect = document.createElement("select"); [5, 10, 25, 50, 100].forEach(n => { const opt = document.createElement("option"); opt.value = n; opt.textContent = n; if (n === this.rowsPerPage) opt.selected = true; this.entriesSelect.appendChild(opt); }); this.entriesSelect.addEventListener("change", () => { this.rowsPerPage = parseInt(this.entriesSelect.value); this.currentPage = 1; this.render(); }); // Search input this.searchInput = document.createElement("input"); this.searchInput.type = "text"; this.searchInput.placeholder = "Search..."; this.searchInput.addEventListener("input", () => { this.searchTerm = this.searchInput.value.toLowerCase(); this.currentPage = 1; this.render(); }); this.controls.appendChild(this.entriesSelect); this.controls.appendChild(this.searchInput); // Table footer (info + pagination) this.footer = document.createElement("div"); this.footer.className = "table-footer"; this.info = document.createElement("div"); this.info.className = "table-info"; this.pagination = document.createElement("ul"); this.pagination.className = "pagination"; this.footer.appendChild(this.info); this.footer.appendChild(this.pagination); // Wrap table in a scroll container (JS dynamic) this.scrollContainer = document.createElement("div"); this.scrollContainer.className = "table-scroll"; this.table.parentNode.insertBefore(this.scrollContainer, this.table); this.scrollContainer.appendChild(this.table); // Insert controls and footer wrapper.insertBefore(this.controls, this.scrollContainer); wrapper.appendChild(this.footer); } attachSortHandlers() { this.table.querySelectorAll("th").forEach((th, i) => { th.addEventListener("click", () => { if (this.sortIndex === i) { this.sortOrder = this.sortOrder === "asc" ? "desc" : "asc"; } else { this.sortIndex = i; this.sortOrder = "asc"; } this.render(); }); }); } render() { let filtered = this.rows.filter(row => row.innerText.toLowerCase().includes(this.searchTerm) ); if (this.sortIndex !== null) { filtered.sort((a, b) => { let textA = a.children[this.sortIndex].innerText.trim(); let textB = b.children[this.sortIndex].innerText.trim(); let numA = parseFloat(textA.replace(/[^0-9.-]+/g, "")); let numB = parseFloat(textB.replace(/[^0-9.-]+/g, "")); if (!isNaN(numA) && !isNaN(numB)) { textA = numA; textB = numB; } if (this.sortOrder === "asc") return textA > textB ? 1 : -1; else return textA < textB ? 1 : -1; }); } const totalRows = filtered.length; const totalPages = Math.ceil(totalRows / this.rowsPerPage); const start = (this.currentPage - 1) * this.rowsPerPage; const end = Math.min(start + this.rowsPerPage, totalRows); this.tbody.innerHTML = ""; if (totalRows === 0) { // Show "no data" row with image + message const tr = document.createElement("tr"); const td = document.createElement("td"); td.colSpan = this.table.querySelectorAll("th").length; td.className = "text-center p-4"; td.innerHTML = `
No Data
No records found
`; tr.appendChild(td); this.tbody.appendChild(tr); this.renderPagination(1); this.renderInfo(0, 0, 0); return; } filtered.slice(start, end).forEach(row => this.tbody.appendChild(row)); this.renderPagination(totalPages); this.renderInfo(start, end, totalRows); this.updateSortIcons(); } renderPagination(totalPages) { this.pagination.innerHTML = ""; const createPageLink = (text, page, disabled = false, active = false) => { const li = document.createElement("li"); const a = document.createElement("a"); a.href = "#"; a.textContent = text; if (disabled) a.style.pointerEvents = "none"; if (active) a.classList.add("active"); a.addEventListener("click", e => { e.preventDefault(); if (!disabled && this.currentPage !== page) { this.currentPage = page; this.render(); } }); li.appendChild(a); return li; }; // First page link this.pagination.appendChild(createPageLink("«", 1, this.currentPage === 1)); // Previous page link this.pagination.appendChild(createPageLink("‹", this.currentPage - 1, this.currentPage === 1)); // Determine page numbers to show (max 5 pages at a time) let startPage = Math.max(1, this.currentPage - 2); let endPage = Math.min(totalPages, startPage + 4); if (endPage - startPage < 4) startPage = Math.max(1, endPage - 4); for (let i = startPage; i <= endPage; i++) { this.pagination.appendChild(createPageLink(i, i, false, i === this.currentPage)); } // Next page link this.pagination.appendChild(createPageLink("›", this.currentPage + 1, this.currentPage === totalPages)); // Last page link this.pagination.appendChild(createPageLink("»", totalPages, this.currentPage === totalPages)); } renderInfo(start,end,total) { this.info.textContent = total===0? "Showing 0 to 0 of 0 entries" : `Showing ${start+1} to ${end} of ${total} entries`; } updateSortIcons() { this.table.querySelectorAll("th").forEach((th,i)=>{ th.classList.remove("asc","desc"); if(i===this.sortIndex) th.classList.add(this.sortOrder); }); } } document.addEventListener("DOMContentLoaded", () => { document.querySelectorAll(".smart-table").forEach(t => new SmartTable(t)); });