document.querySelectorAll("[data-admin-table]").forEach((tableShell) => { const table = tableShell.querySelector("table"); const tbody = tableShell.querySelector("tbody"); const search = tableShell.querySelector("[data-table-search]"); const pageSize = tableShell.querySelector("[data-table-page-size]"); const count = tableShell.querySelector("[data-table-count]"); const pagination = tableShell.querySelector("[data-table-pagination]"); if (!table || !tbody || !search || !pageSize || !count || !pagination) return; const headerCount = table.querySelectorAll("thead th").length || 1; const originalRows = Array.from(tbody.querySelectorAll("tr")); const dataRows = originalRows.filter((row) => !row.classList.contains("empty-row")); let currentPage = 1; const emptyRow = (message) => { const row = document.createElement("tr"); row.className = "empty-row"; const cell = document.createElement("td"); cell.className = "empty"; cell.colSpan = headerCount; const state = document.createElement("div"); state.className = "empty-state"; const title = document.createElement("div"); title.className = "empty-state-title"; title.textContent = message; state.append(title); cell.append(state); row.append(cell); return row; }; const visibleRows = () => { const query = search.value.trim().toLowerCase(); if (!query) return dataRows; return dataRows.filter((row) => row.textContent.toLowerCase().includes(query)); }; const renderButton = (label, page, disabled = false, active = false) => { const item = document.createElement("li"); item.className = `page-item${disabled ? " disabled" : ""}${active ? " active" : ""}`; const button = document.createElement("button"); button.type = "button"; button.className = "page-link"; button.textContent = label; button.disabled = disabled; button.addEventListener("click", () => { currentPage = page; render(); }); item.append(button); return item; }; const renderPagination = (totalPages) => { pagination.replaceChildren(); if (totalPages <= 1) return; pagination.append(renderButton("Previous", Math.max(1, currentPage - 1), currentPage === 1)); for (let page = 1; page <= totalPages; page += 1) { if (totalPages > 7 && page !== 1 && page !== totalPages && Math.abs(page - currentPage) > 1) { if (page === 2 || page === totalPages - 1) pagination.append(renderButton("...", currentPage, true)); continue; } pagination.append(renderButton(String(page), page, false, page === currentPage)); } pagination.append(renderButton("Next", Math.min(totalPages, currentPage + 1), currentPage === totalPages)); }; const render = () => { const rows = visibleRows(); const size = Number.parseInt(pageSize.value, 10) || 25; const totalPages = Math.max(1, Math.ceil(rows.length / size)); currentPage = Math.min(currentPage, totalPages); const start = (currentPage - 1) * size; const pageRows = rows.slice(start, start + size); tbody.replaceChildren( ...(pageRows.length ? pageRows : [emptyRow(dataRows.length ? "No matching rows." : "No rows yet.")]) ); const shownStart = rows.length ? start + 1 : 0; const shownEnd = rows.length ? Math.min(start + size, rows.length) : 0; count.textContent = `${shownStart}-${shownEnd} of ${rows.length}`; renderPagination(totalPages); }; search.addEventListener("input", () => { currentPage = 1; render(); }); pageSize.addEventListener("change", () => { currentPage = 1; render(); }); render(); });