Skip to content

Commit 6794de9

Browse files
Add paging and per-column filters to PR table
Remove hardcoded site title from gh-pages template config and enhance the PR table fallback UI: replace simple column-filtering with a paginated, client-side filtering UI. Adds page size selector (10/25/50/100, default 25), info text, pagination controls, and logic to compute filtered rows and render the current page. Keeps the Created-date sort fallback but improves usability for large PR lists.
1 parent 04fc9ca commit 6794de9

2 files changed

Lines changed: 79 additions & 6 deletions

File tree

gh-pages-template/_config.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
---
2-
title: LizardByte Developer Dashboard
32
site-js: [] # disable crowdin for this site

gh-pages-template/assets/js/dashboard.js

Lines changed: 79 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ function renderPRTable(prs) {
186186
return dt;
187187
}
188188

189-
// Fallback: sort by Created date (col 7) ascending, then wire up pure-JS column filters.
189+
// Fallback: sort by Created date (col 7) ascending, then add paging + per-column filtering.
190190
const allRows = Array.from(tbody.querySelectorAll('tr'));
191191
allRows.sort((a, b) => {
192192
const aVal = a.querySelectorAll('td')[7]?.textContent || '';
@@ -195,14 +195,88 @@ function renderPRTable(prs) {
195195
});
196196
allRows.forEach(row => tbody.appendChild(row));
197197

198-
function applyFilters() {
198+
let pageSize = 25;
199+
let currentPage = 0;
200+
201+
const lengthSel = document.createElement('select');
202+
lengthSel.className = 'form-select form-select-sm d-inline-block w-auto';
203+
[10, 25, 50, 100].forEach(n => {
204+
const opt = document.createElement('option');
205+
opt.value = String(n);
206+
opt.textContent = String(n);
207+
if (n === 25) opt.selected = true;
208+
lengthSel.appendChild(opt);
209+
});
210+
const lengthLabel = document.createElement('label');
211+
lengthLabel.className = 'text-muted small me-auto';
212+
lengthLabel.append('Show ', lengthSel, ' entries');
213+
214+
const infoEl = document.createElement('small');
215+
infoEl.className = 'text-muted';
216+
217+
const pagUl = document.createElement('ul');
218+
pagUl.className = 'pagination pagination-sm mb-0';
219+
220+
const topBar = document.createElement('div');
221+
topBar.className = 'd-flex align-items-center mb-2';
222+
topBar.appendChild(lengthLabel);
223+
224+
const bottomBar = document.createElement('div');
225+
bottomBar.className = 'd-flex justify-content-between align-items-center mt-2 flex-wrap gap-2';
226+
bottomBar.append(infoEl, pagUl);
227+
228+
const table = container.querySelector('table');
229+
container.insertBefore(topBar, table);
230+
container.appendChild(bottomBar);
231+
232+
function getFiltered() {
199233
const filters = filterInputs.map(inp => inp.value.toLowerCase());
200-
allRows.forEach(row => {
234+
return allRows.filter(row => {
201235
const cells = Array.from(row.querySelectorAll('td'));
202-
row.style.display = filters.every((f, i) => !f || (cells[i]?.textContent || '').toLowerCase().includes(f)) ? '' : 'none';
236+
return filters.every((f, i) => !f || (cells[i]?.textContent || '').toLowerCase().includes(f));
203237
});
204238
}
205-
filterInputs.forEach(inp => inp.addEventListener('input', applyFilters));
239+
240+
function renderPage() {
241+
const filtered = getFiltered();
242+
const totalPages = Math.max(1, Math.ceil(filtered.length / pageSize));
243+
currentPage = Math.min(currentPage, totalPages - 1);
244+
const start = currentPage * pageSize;
245+
246+
allRows.forEach(r => { r.style.display = 'none'; });
247+
filtered.slice(start, start + pageSize).forEach(r => { r.style.display = ''; });
248+
249+
const from = filtered.length === 0 ? 0 : start + 1;
250+
const to = Math.min(start + pageSize, filtered.length);
251+
infoEl.textContent = `Showing ${from} to ${to} of ${filtered.length} entries`;
252+
253+
pagUl.innerHTML = '';
254+
const addBtn = (label, page, disabled, active) => {
255+
const li = document.createElement('li');
256+
li.className = `page-item${disabled ? ' disabled' : ''}${active ? ' active' : ''}`;
257+
const btn = document.createElement('button');
258+
btn.className = 'page-link';
259+
btn.textContent = label;
260+
btn.disabled = disabled;
261+
if (!disabled) btn.addEventListener('click', () => { currentPage = page; renderPage(); });
262+
li.appendChild(btn);
263+
pagUl.appendChild(li);
264+
};
265+
addBtn('Previous', currentPage - 1, currentPage === 0, false);
266+
const startP = Math.max(0, Math.min(currentPage - 2, totalPages - 5));
267+
for (let p = startP; p < Math.min(totalPages, startP + 5); p++) {
268+
addBtn(String(p + 1), p, false, p === currentPage);
269+
}
270+
addBtn('Next', currentPage + 1, currentPage >= totalPages - 1, false);
271+
}
272+
273+
lengthSel.addEventListener('change', () => {
274+
pageSize = parseInt(lengthSel.value, 10);
275+
currentPage = 0;
276+
renderPage();
277+
});
278+
filterInputs.forEach(inp => inp.addEventListener('input', () => { currentPage = 0; renderPage(); }));
279+
renderPage();
206280
}
207281

208282
// License treemap

0 commit comments

Comments
 (0)