From a956998531bb7aa8d921971eb8b977323c5f5b97 Mon Sep 17 00:00:00 2001 From: Yashaswini K P Date: Sun, 14 Jun 2026 17:38:05 +0530 Subject: [PATCH 1/6] added non-activity section --- frontend/leaderboard.html | 105 ++++++++++++++++++++++-------------- scripts/sync-leaderboard.js | 10 ++-- 2 files changed, 71 insertions(+), 44 deletions(-) diff --git a/frontend/leaderboard.html b/frontend/leaderboard.html index 932e40f6..31c19d1f 100644 --- a/frontend/leaderboard.html +++ b/frontend/leaderboard.html @@ -303,20 +303,27 @@

Leaderboard

countEl.textContent = ""; countEl.style.opacity = "0"; } - document.getElementById("prev-page-btn").disabled = currentPage === 1; + const renderableData = + activeDatasetType === "overall" + ? filteredData.filter((user) => user.score > 0) + : filteredData; + + const totalPages = Math.ceil(renderableData.length / itemsPerPage) || 1; + 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"); + currentPage === totalPages; - const totalPages = Math.ceil(filteredData.length / itemsPerPage); + const statsEl = document.getElementById("leaderboard-stats"); const startRow = - filteredData.length === 0 ? 0 : (currentPage - 1) * itemsPerPage + 1; + renderableData.length === 0 + ? 0 + : (currentPage - 1) * itemsPerPage + 1; const endRow = Math.min( currentPage * itemsPerPage, - filteredData.length, + renderableData.length, ); statsEl.innerHTML = ` @@ -326,15 +333,15 @@

Leaderboard

margin-bottom:1rem; font-family:'Fira Code', monospace; "> - Total Users: ${filteredData.length} + Total Users: ${renderableData.length} | Showing: ${startRow}-${endRow} | Page: ${currentPage}/${totalPages} `; - renderLeaderboard(filteredData); + renderLeaderboard(filteredData, totalPages); } - function renderLeaderboard(data) { + function renderLeaderboard(data, totalPages) { const body = document.getElementById("leaderboard-body"); const mobileCards = document.getElementById("mobile-cards"); @@ -346,49 +353,65 @@

Leaderboard

const startIndex = (currentPage - 1) * itemsPerPage; const endIndex = startIndex + itemsPerPage; - const displayData = isSearching - ? data - : data.slice(startIndex, endIndex); - - if (displayData.length === 0) { - body.innerHTML = ` -
- [SYS]: NO_MATCHING_USERS_FOUND -
- `; - - mobileCards.innerHTML = ` -
- [SYS]: NO_MATCHING_USERS_FOUND -
- `; + let activeList = data; + let inactiveList = []; + + if (activeDatasetType === "overall") { + activeList = data.filter((user) => user.score > 0); + inactiveList = data.filter((user) => user.score === 0); + } + + const displayActiveData = + isSearching || activeDatasetType !== "overall" + ? activeList + : activeList.slice(startIndex, endIndex); + + if (displayActiveData.length === 0 && inactiveList.length === 0) { + const emptyStateHtml = `
[SYS]: NO_MATCHING_USERS_FOUND
`; + body.innerHTML = emptyStateHtml; + mobileCards.innerHTML = emptyStateHtml; return; } - displayData.forEach((user, index) => { + displayActiveData.forEach((user, index) => { const rank = user.originalRank !== undefined ? user.originalRank : startIndex + index + 1; - const row = renderLeaderboardRow(user, rank); - const card = renderMobileCard(user, rank); - body.appendChild(row); - mobileCards.appendChild(card); - }); - renderPagination(data.length); - } - function setActiveTab(activeTab) { - document.querySelectorAll(".tab").forEach((tab) => { - tab.classList.remove("active"); - if (tab.dataset.tab === activeTab) { - tab.classList.add("active"); - } + body.appendChild(renderLeaderboardRow(user, rank)); + mobileCards.appendChild(renderMobileCard(user, rank)); }); - if (!leaderboardData[activeTab]) { - return; + if ( + activeDatasetType === "overall" && + inactiveList.length > 0 && + (currentPage === totalPages || isSearching) + ) { + const dividerText = "── [ SECTION: NO ACTIVITY YET ] ──"; + + const tableHeader = document.createElement("div"); + tableHeader.className = "no-results"; + tableHeader.style.gridColumn = "1 / -1"; + tableHeader.innerText = dividerText; + body.appendChild(tableHeader); + + const mobileHeader = document.createElement("div"); + mobileHeader.className = "no-results"; + mobileHeader.innerText = dividerText; + mobileCards.appendChild(mobileHeader); + + inactiveList.forEach((user) => { + body.appendChild(renderLeaderboardRow(user, "--")); + mobileCards.appendChild(renderMobileCard(user, "--")); + }); } + renderPagination(activeList.length); + } + + function setActiveTab(activeTab) { + if (!leaderboardData[activeTab]) return; + activeDatasetType = activeTab; currentPage = 1; applyFiltersAndRender(); diff --git a/scripts/sync-leaderboard.js b/scripts/sync-leaderboard.js index 105698e7..79073617 100644 --- a/scripts/sync-leaderboard.js +++ b/scripts/sync-leaderboard.js @@ -34,10 +34,14 @@ function getFileName(daysAgo) { function assignCompetitionRanks(sortedData) { let currentRank = 1; for (let i = 0; i < sortedData.length; i++) { - if (i > 0 && sortedData[i].score < sortedData[i - 1].score) { - currentRank = i + 1; + if (sortedData[i].score === 0) { + sortedData[i].originalRank = "--"; + } else { + if (i > 0 && sortedData[i].score < sortedData[i - 1].score) { + currentRank = i + 1; + } + sortedData[i].originalRank = currentRank; } - sortedData[i].originalRank = currentRank; } } From fe4849a3319c5c32eaffbae36db1de711d8ad9f7 Mon Sep 17 00:00:00 2001 From: Yashaswini K P Date: Mon, 15 Jun 2026 21:53:05 +0530 Subject: [PATCH 2/6] adding no-activity section to all tabs --- frontend/leaderboard.html | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/frontend/leaderboard.html b/frontend/leaderboard.html index a09b1cf8..2728c7bb 100644 --- a/frontend/leaderboard.html +++ b/frontend/leaderboard.html @@ -401,10 +401,12 @@

Leaderboard

countEl.style.opacity = "0"; } - const renderableData = - activeDatasetType === "overall" - ? filteredData.filter((user) => user.score > 0) - : filteredData; + const overallDataset = window.leaderboardData["overall"] || originalData || []; + const zeroScoreUserIds = new Set( + overallDataset.filter(user => user.score === 0).map(user => user.id) + ); + + const renderableData = filteredData.filter((user) => !zeroScoreUserIds.has(user.id)); const totalPages = Math.ceil(renderableData.length / itemsPerPage) || 1; document.getElementById("prev-page-btn").disabled = currentPage === 1; @@ -430,15 +432,16 @@

Leaderboard

margin-bottom:1rem; font-family:'Fira Code', monospace; "> - Total Users: ${renderableData.length} + Total Users: ${filteredData.length} | Showing: ${startRow}-${endRow} | Page: ${currentPage}/${totalPages} `; - renderLeaderboard(filteredData, totalPages); + + renderLeaderboard(filteredData, zeroScoreUserIds, totalPages); } - function renderLeaderboard(data, totalPages) { + function renderLeaderboard(data, zeroScoreUserIds, totalPages) { const body = document.getElementById("leaderboard-body"); const mobileCards = document.getElementById("mobile-cards"); @@ -450,13 +453,8 @@

Leaderboard

const startIndex = (currentPage - 1) * itemsPerPage; const endIndex = startIndex + itemsPerPage; - let activeList = data; - let inactiveList = []; - - if (activeDatasetType === "overall") { - activeList = data.filter((user) => user.score > 0); - inactiveList = data.filter((user) => user.score === 0); - } + const activeList = data.filter((user) => !zeroScoreUserIds.has(user.id)); + const inactiveList = data.filter((user) => zeroScoreUserIds.has(user.id)); const displayActiveData = isSearching || activeDatasetType !== "overall" @@ -471,16 +469,20 @@

Leaderboard

} displayActiveData.forEach((user, index) => { - const rank = + let rank = user.originalRank !== undefined ? user.originalRank : startIndex + index + 1; + + if (activeDatasetType !== "overall" && user.score === 0) { + rank = "--"; + } + body.appendChild(renderLeaderboardRow(user, rank)); mobileCards.appendChild(renderMobileCard(user, rank)); }); if ( - activeDatasetType === "overall" && inactiveList.length > 0 && (currentPage === totalPages || isSearching) ) { From fd4cc6ba2ac5c612b2824979e838309ea0e047ae Mon Sep 17 00:00:00 2001 From: Yashaswini K P Date: Mon, 15 Jun 2026 22:18:06 +0530 Subject: [PATCH 3/6] fix tab switching pagination and clean up inactive ranks --- frontend/leaderboard.html | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/frontend/leaderboard.html b/frontend/leaderboard.html index 2728c7bb..c02de3ff 100644 --- a/frontend/leaderboard.html +++ b/frontend/leaderboard.html @@ -401,12 +401,15 @@

Leaderboard

countEl.style.opacity = "0"; } - const overallDataset = window.leaderboardData["overall"] || originalData || []; + const overallDataset = window.leaderboardData && window.leaderboardData["overall"] + ? window.leaderboardData["overall"] + : (originalData || []); + const zeroScoreUserIds = new Set( - overallDataset.filter(user => user.score === 0).map(user => user.id) + overallDataset.filter(user => (user && user.score === 0)).map(user => user.id) ); - const renderableData = filteredData.filter((user) => !zeroScoreUserIds.has(user.id)); + const renderableData = filteredData.filter((user) => user && !zeroScoreUserIds.has(user.id)); const totalPages = Math.ceil(renderableData.length / itemsPerPage) || 1; document.getElementById("prev-page-btn").disabled = currentPage === 1; @@ -456,10 +459,9 @@

Leaderboard

const activeList = data.filter((user) => !zeroScoreUserIds.has(user.id)); const inactiveList = data.filter((user) => zeroScoreUserIds.has(user.id)); - const displayActiveData = - isSearching || activeDatasetType !== "overall" - ? activeList - : activeList.slice(startIndex, endIndex); + const displayActiveData = isSearching + ? activeList + : activeList.slice(startIndex, endIndex); if (displayActiveData.length === 0 && inactiveList.length === 0) { const emptyStateHtml = `
[SYS]: NO_MATCHING_USERS_FOUND
`; @@ -500,8 +502,8 @@

Leaderboard

mobileCards.appendChild(mobileHeader); inactiveList.forEach((user) => { - body.appendChild(renderLeaderboardRow(user, "--")); - mobileCards.appendChild(renderMobileCard(user, "--")); + body.appendChild(renderLeaderboardRow(user, "")); + mobileCards.appendChild(renderMobileCard(user, "")); }); } @@ -515,6 +517,15 @@

Leaderboard

activeDatasetType = activeTab; currentPage = 1; + + document.querySelectorAll(".tab").forEach((tab) => { + if (tab.dataset.tab === activeTab) { + tab.classList.add("active"); + } else { + tab.classList.remove("active"); + } + }); + applyFiltersAndRender(); } From 35e75644f4f4d1f3d906e4a036deb0f3f5ea4992 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 15 Jun 2026 16:49:35 +0000 Subject: [PATCH 4/6] style: auto-format code with Prettier (/format) --- frontend/leaderboard.html | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/frontend/leaderboard.html b/frontend/leaderboard.html index c02de3ff..3c4345df 100644 --- a/frontend/leaderboard.html +++ b/frontend/leaderboard.html @@ -401,15 +401,20 @@

Leaderboard

countEl.style.opacity = "0"; } - const overallDataset = window.leaderboardData && window.leaderboardData["overall"] - ? window.leaderboardData["overall"] - : (originalData || []); + const overallDataset = + window.leaderboardData && window.leaderboardData["overall"] + ? window.leaderboardData["overall"] + : originalData || []; const zeroScoreUserIds = new Set( - overallDataset.filter(user => (user && user.score === 0)).map(user => user.id) + overallDataset + .filter((user) => user && user.score === 0) + .map((user) => user.id), ); - const renderableData = filteredData.filter((user) => user && !zeroScoreUserIds.has(user.id)); + const renderableData = filteredData.filter( + (user) => user && !zeroScoreUserIds.has(user.id), + ); const totalPages = Math.ceil(renderableData.length / itemsPerPage) || 1; document.getElementById("prev-page-btn").disabled = currentPage === 1; @@ -440,7 +445,7 @@

Leaderboard

| Page: ${currentPage}/${totalPages} `; - + renderLeaderboard(filteredData, zeroScoreUserIds, totalPages); } @@ -456,11 +461,15 @@

Leaderboard

const startIndex = (currentPage - 1) * itemsPerPage; const endIndex = startIndex + itemsPerPage; - const activeList = data.filter((user) => !zeroScoreUserIds.has(user.id)); - const inactiveList = data.filter((user) => zeroScoreUserIds.has(user.id)); + const activeList = data.filter( + (user) => !zeroScoreUserIds.has(user.id), + ); + const inactiveList = data.filter((user) => + zeroScoreUserIds.has(user.id), + ); - const displayActiveData = isSearching - ? activeList + const displayActiveData = isSearching + ? activeList : activeList.slice(startIndex, endIndex); if (displayActiveData.length === 0 && inactiveList.length === 0) { @@ -475,7 +484,7 @@

Leaderboard

user.originalRank !== undefined ? user.originalRank : startIndex + index + 1; - + if (activeDatasetType !== "overall" && user.score === 0) { rank = "--"; } From 45a34abf3bdefd818671f7e2fcdecbde16ce2269 Mon Sep 17 00:00:00 2001 From: Jagdish Prajapati Date: Tue, 16 Jun 2026 01:31:58 +0530 Subject: [PATCH 5/6] Handle idle and empty ranks in leaderboard rendering --- frontend/js/leaderboard/render.js | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/frontend/js/leaderboard/render.js b/frontend/js/leaderboard/render.js index 4b0ffe1b..c23bb9d6 100644 --- a/frontend/js/leaderboard/render.js +++ b/frontend/js/leaderboard/render.js @@ -100,7 +100,7 @@ function createExternalIcon() { function renderLeaderboardRow(user, rank) { const rankTagEl = createRankTagElement(rank); - const rankChangeEl = createRankChangeElement(user.rankChange); + const rankChangeEl = user.score > 0 ? createRankChangeElement(user.rankChange) : null; const easyPoints = 1; const mediumPoints = 3; const hardPoints = 5; @@ -136,7 +136,16 @@ function renderLeaderboardRow(user, rank) { // Rank (numeric — safe) const rankDiv = document.createElement("div"); rankDiv.className = "rank"; - rankDiv.textContent = rank; + if (rank === "") { + rankDiv.textContent = "[IDLE]"; + rankDiv.style.color = "var(--text-dim)"; + rankDiv.style.fontSize = "0.75rem"; + } else if (rank === "--") { + rankDiv.textContent = "[--]"; + rankDiv.style.color = "var(--text-dim)"; + } else { + rankDiv.textContent = rank; + } row.appendChild(rankDiv); // Name cell — tag is safe DOM element, name is user-controlled (textContent) @@ -260,7 +269,16 @@ function renderMobileCard(user, rank) { const mobileRank = document.createElement("div"); mobileRank.className = "mobile-rank"; - mobileRank.textContent = `#${rank}`; + if (rank === "") { + mobileRank.textContent = "[IDLE]"; + mobileRank.style.color = "var(--text-dim)"; + mobileRank.style.fontSize = "0.75rem"; + } else if (rank === "--") { + mobileRank.textContent = "[--]"; + mobileRank.style.color = "var(--text-dim)"; + } else { + mobileRank.textContent = `#${rank}`; + } cardHeader.appendChild(mobileRank); const mobileScore = document.createElement("div"); @@ -293,7 +311,7 @@ function renderMobileCard(user, rank) { const mobileName = document.createElement("div"); mobileName.className = "mobile-name"; const mobileRankTagEl = createRankTagElement(rank); - const mobileRankChangeEl = createRankChangeElement(user.rankChange); + const mobileRankChangeEl = user.score > 0 ? createRankChangeElement(user.rankChange) : null; if (mobileRankTagEl) { mobileName.appendChild(mobileRankTagEl); } From 91750e3c38df3fadf905250bbd6d0aa85d2cd2ae Mon Sep 17 00:00:00 2001 From: Jagdish Prajapati Date: Tue, 16 Jun 2026 01:32:36 +0530 Subject: [PATCH 6/6] Hide leaderboard rows and no-results in CSS --- frontend/styles/main.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/styles/main.css b/frontend/styles/main.css index d9fcf7e8..b3a46993 100644 --- a/frontend/styles/main.css +++ b/frontend/styles/main.css @@ -1574,7 +1574,8 @@ body.crt-scrolling { } .leaderboard-header, - .leaderboard-row { + .leaderboard-row, + .leaderboard .no-results { display: none; }