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

+
+
Rank
@@ -125,32 +127,17 @@

Leaderboard

flex-wrap: wrap; " > - - + + + + +
+ +
@@ -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; +}