diff --git a/frontend/leaderboard.html b/frontend/leaderboard.html
index 09fd96b6..ccca0d2b 100644
--- a/frontend/leaderboard.html
+++ b/frontend/leaderboard.html
@@ -86,6 +86,8 @@
Leaderboard
+
+
@@ -194,36 +181,44 @@ Leaderboard
applyFiltersAndRender();
}
});
+ document
+ .getElementById("prev-page-btn")
+ ?.addEventListener("click", () => {
+ if (currentPage > 1) {
+ currentPage--;
+ applyFiltersAndRender();
+ }
+ });
+ document
+ .getElementById("next-page-btn")
+ ?.addEventListener("click", () => {
+ const totalPages = Math.ceil(
+ leaderboardData[activeDatasetType].length / itemsPerPage,
+ );
+
+ if (currentPage < totalPages) {
+ currentPage++;
+ applyFiltersAndRender();
+ }
+ });
+
+ document
+ .getElementById("items-per-page")
+ ?.addEventListener("change", (e) => {
+ itemsPerPage = parseInt(e.target.value);
+ currentPage = 1;
+ applyFiltersAndRender();
+ });
fetchLeaderboardData();
});
const leaderboardData = {};
- let currentDisplayLimit = 0;
+ let currentPage = 1;
+ let itemsPerPage = 25;
let activeDatasetType = "overall";
- let fetchMoreClicks = 0;
let currentSearchTerm = "";
- function getInitialLimit() {
- return window.innerWidth <= 768 ? 12 : 25;
- }
-
- function loadMoreNodes() {
- const data = leaderboardData[activeDatasetType];
- const isMobile = window.innerWidth <= 768;
- const maxClicks = isMobile ? 3 : 2;
-
- fetchMoreClicks++;
-
- if (fetchMoreClicks >= maxClicks) {
- currentDisplayLimit = data.length;
- } else {
- currentDisplayLimit += getInitialLimit();
- }
-
- renderLeaderboard(data);
- }
-
async function fetchLeaderboardData() {
const endpoints = ["overall", "monthly", "weekly", "daily"];
const cacheBuster = Date.now();
@@ -294,9 +289,6 @@ Leaderboard
}
function applyFiltersAndRender() {
- if (currentDisplayLimit === 0) {
- currentDisplayLimit = getInitialLimit();
- }
if (!leaderboardData[activeDatasetType]) return;
const originalData = leaderboardData[activeDatasetType];
@@ -308,27 +300,55 @@ Leaderboard
const filteredData = rankedData.filter((user) => {
if (!currentSearchTerm) return true;
+
return (
user.name.toLowerCase().includes(currentSearchTerm) ||
user.id.toLowerCase().includes(currentSearchTerm)
);
});
- // Update result count
const countEl = document.getElementById("search-result-count");
+
if (currentSearchTerm) {
const total = originalData.length;
const matched = filteredData.length;
+
countEl.textContent = `${matched}/${total}`;
countEl.style.opacity = "1";
} else {
countEl.textContent = "";
countEl.style.opacity = "0";
}
-
+ document.getElementById("prev-page-btn").disabled = currentPage === 1;
+
+ document.getElementById("next-page-btn").disabled =
+ currentPage === Math.ceil(filteredData.length / itemsPerPage);
+ const statsEl = document.getElementById("leaderboard-stats");
+
+ const totalPages = Math.ceil(filteredData.length / itemsPerPage);
+
+ const startRow =
+ filteredData.length === 0 ? 0 : (currentPage - 1) * itemsPerPage + 1;
+
+ const endRow = Math.min(
+ currentPage * itemsPerPage,
+ filteredData.length,
+ );
+
+ statsEl.innerHTML = `
+
+ Total Users: ${filteredData.length}
+ | Showing: ${startRow}-${endRow}
+ | Page: ${currentPage}/${totalPages}
+
+ `;
renderLeaderboard(filteredData);
}
-
function getRankTag(rank) {
switch (rank) {
case 1:
@@ -351,9 +371,12 @@ Leaderboard
const isSearching = currentSearchTerm.length > 0;
+ const startIndex = (currentPage - 1) * itemsPerPage;
+ const endIndex = startIndex + itemsPerPage;
+
const displayData = isSearching
? data
- : data.slice(0, currentDisplayLimit);
+ : data.slice(startIndex, endIndex);
if (displayData.length === 0) {
body.innerHTML = `
@@ -367,10 +390,6 @@ Leaderboard
[SYS]: NO_MATCHING_USERS_FOUND
`;
-
- document.getElementById("load-more-btn").style.display = "none";
- document.getElementById("scroll-top-btn").style.display = "none";
-
return;
}
@@ -418,30 +437,34 @@ Leaderboard
`;
mobileCards.appendChild(card);
});
+ renderPagination(data.length);
+ }
+ function renderPagination(totalItems) {
+ const pageNumbers = document.getElementById("page-numbers");
- if (!isSearching && data.length > currentDisplayLimit) {
- document.getElementById("load-more-btn").style.display =
- "inline-block";
- document.getElementById("scroll-top-btn").style.display = "none";
+ if (!pageNumbers) return;
- const isMobile = window.innerWidth <= 768;
- const textTransitionClick = isMobile ? 2 : 1;
+ const totalPages = Math.ceil(totalItems / itemsPerPage);
- if (fetchMoreClicks >= textTransitionClick) {
- document.getElementById("load-more-btn").innerHTML =
- "./fetch_all_nodes";
- } else {
- document.getElementById("load-more-btn").innerHTML =
- "./fetch_more_nodes";
+ pageNumbers.innerHTML = "";
+
+ for (let i = 1; i <= totalPages; i++) {
+ const btn = document.createElement("button");
+ btn.classList.add("page-btn");
+ btn.textContent = i;
+
+ if (i === currentPage) {
+ btn.classList.add("active-page");
}
- } else {
- document.getElementById("load-more-btn").style.display = "none";
- document.getElementById("scroll-top-btn").style.display = isSearching
- ? "none"
- : "inline-block";
+
+ btn.onclick = () => {
+ currentPage = i;
+ applyFiltersAndRender();
+ };
+
+ pageNumbers.appendChild(btn);
}
}
-
function setActiveTab(activeTab) {
document.querySelectorAll(".tab").forEach((tab) => {
tab.classList.remove("active");
@@ -455,8 +478,7 @@ Leaderboard
}
activeDatasetType = activeTab;
- currentDisplayLimit = getInitialLimit();
- fetchMoreClicks = 0;
+ currentPage = 1;
applyFiltersAndRender();
}
diff --git a/frontend/styles/main.css b/frontend/styles/main.css
index 8f933711..5186e4f1 100644
--- a/frontend/styles/main.css
+++ b/frontend/styles/main.css
@@ -2161,3 +2161,55 @@ body::-webkit-scrollbar-thumb {
font-family: "Fira Code", monospace;
font-size: 0.85rem;
}
+
+/* Pagination Styling */
+
+#pagination-controls {
+ margin-top: 2rem;
+ align-items: center;
+}
+
+#pagination-controls button,
+#pagination-controls select {
+ background: var(--bg-surface);
+ color: var(--green);
+ border: 1px solid var(--border-bright);
+ padding: 0.5rem 0.8rem;
+ font-family: "Fira Code", monospace;
+ cursor: pointer;
+}
+
+#pagination-controls button:hover,
+#pagination-controls select:hover {
+ border-color: var(--green);
+ box-shadow: 0 0 8px rgba(0, 255, 65, 0.3);
+}
+
+#page-numbers {
+ display: flex;
+ gap: 0.4rem;
+}
+
+#page-numbers button {
+ min-width: 32px;
+ height: 32px;
+ background: var(--bg-surface);
+ color: var(--green);
+ border: 1px solid var(--border-bright);
+ cursor: pointer;
+}
+
+#page-numbers button:hover {
+ border-color: var(--green);
+}
+
+.active-page {
+ background: var(--green) !important;
+ color: black !important;
+ font-weight: bold;
+}
+
+#pagination-controls button:disabled {
+ opacity: 0.4;
+ cursor: not-allowed;
+}