Skip to content

Commit 9b6c58f

Browse files
Add leaderboard search functionality (#43)
1 parent 7702cf0 commit 9b6c58f

2 files changed

Lines changed: 116 additions & 4 deletions

File tree

frontend/leaderboard.html

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,18 @@ <h1 class="page-title">Leaderboard</h1>
3939
<button class="tab" data-tab="daily">Daily</button>
4040
</div>
4141

42+
43+
44+
<div class="search-container">
45+
<input
46+
type="text"
47+
id="leaderboard-search"
48+
class="search-input"
49+
placeholder="./search_by_name_or_leetcode_id"
50+
autocomplete="off"
51+
>
52+
</div>
53+
4254
<div class="leaderboard">
4355
<div class="leaderboard-header">
4456
<div>Rank</div>
@@ -94,13 +106,21 @@ <h1 class="page-title">Leaderboard</h1>
94106
});
95107
});
96108

109+
const searchInput = document.getElementById('leaderboard-search');
110+
111+
searchInput.addEventListener('input', (e) => {
112+
currentSearchTerm = e.target.value.toLowerCase().trim();
113+
applyFiltersAndRender();
114+
});
115+
97116
fetchLeaderboardData();
98117
});
99118

100119
const leaderboardData = {};
101120
let currentDisplayLimit = 0;
102121
let activeDatasetType = 'overall';
103122
let fetchMoreClicks = 0;
123+
let currentSearchTerm = '';
104124

105125
function getInitialLimit() {
106126
return window.innerWidth <= 768 ? 12 : 25;
@@ -177,7 +197,30 @@ <h1 class="page-title">Leaderboard</h1>
177197
document.getElementById('next-sync-time').style.color = "var(--red)";
178198
}
179199

180-
setActiveTab('overall');
200+
applyFiltersAndRender();
201+
}
202+
203+
function applyFiltersAndRender() {
204+
if (currentDisplayLimit === 0) {
205+
currentDisplayLimit = getInitialLimit();
206+
}
207+
if (!leaderboardData[activeDatasetType]) return;
208+
209+
const originalData = leaderboardData[activeDatasetType];
210+
211+
const rankedData = originalData.map((user, index) => ({
212+
...user,
213+
originalRank: index + 1
214+
}));
215+
216+
const filteredData = rankedData.filter(user => {
217+
return (
218+
user.name.toLowerCase().includes(currentSearchTerm) ||
219+
user.id.toLowerCase().includes(currentSearchTerm)
220+
);
221+
});
222+
223+
renderLeaderboard(filteredData);
181224
}
182225

183226
function getRankTag(rank) {
@@ -196,10 +239,33 @@ <h1 class="page-title">Leaderboard</h1>
196239
body.innerHTML = '';
197240
mobileCards.innerHTML = '';
198241

199-
const displayData = data.slice(0, currentDisplayLimit);
242+
const isSearching = currentSearchTerm.length > 0;
243+
244+
const displayData = isSearching
245+
? data
246+
: data.slice(0, currentDisplayLimit);
247+
248+
if (displayData.length === 0) {
249+
body.innerHTML = `
250+
<div class="no-results">
251+
[SYS]: NO_MATCHING_USERS_FOUND
252+
</div>
253+
`;
254+
255+
mobileCards.innerHTML = `
256+
<div class="no-results">
257+
[SYS]: NO_MATCHING_USERS_FOUND
258+
</div>
259+
`;
260+
261+
document.getElementById('load-more-btn').style.display = 'none';
262+
document.getElementById('scroll-top-btn').style.display = 'none';
263+
264+
return;
265+
}
200266

201267
displayData.forEach((user, index) => {
202-
const rank = index + 1;
268+
const rank = user.originalRank || index + 1;
203269
const tag = getRankTag(rank);
204270
const leetcodeUrl = `https://leetcode.com/u/${user.id}`;
205271

@@ -276,7 +342,7 @@ <h1 class="page-title">Leaderboard</h1>
276342
activeDatasetType = activeTab;
277343
currentDisplayLimit = getInitialLimit();
278344
fetchMoreClicks = 0;
279-
renderLeaderboard(leaderboardData[activeTab]);
345+
applyFiltersAndRender();
280346
}
281347

282348
</script>

frontend/styles/main.css

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1913,3 +1913,49 @@ body::-webkit-scrollbar-thumb {
19131913
.tooltip-content a:hover {
19141914
text-shadow: 0 0 8px rgba(0, 229, 255, 0.4);
19151915
}
1916+
1917+
1918+
1919+
1920+
1921+
1922+
1923+
.search-container {
1924+
display: flex;
1925+
justify-content: center;
1926+
margin-bottom: 1.5rem;
1927+
}
1928+
1929+
.search-input {
1930+
width: 100%;
1931+
max-width: 420px;
1932+
padding: 0.9rem 1rem;
1933+
background: rgba(0, 0, 0, 0.7);
1934+
border: 1px solid var(--cyan);
1935+
color: var(--text);
1936+
font-family: 'Fira Code', monospace;
1937+
font-size: 0.9rem;
1938+
outline: none;
1939+
transition: all 0.2s ease;
1940+
box-shadow: 0 0 10px rgba(0, 229, 255, 0.08);
1941+
}
1942+
1943+
.search-input::placeholder {
1944+
color: var(--text-muted);
1945+
font-family: 'Fira Code', monospace;
1946+
}
1947+
1948+
.search-input:focus {
1949+
border-color: var(--green);
1950+
box-shadow: 0 0 12px rgba(0, 255, 100, 0.2);
1951+
}
1952+
1953+
.no-results {
1954+
text-align: center;
1955+
padding: 2rem;
1956+
color: var(--text-muted);
1957+
font-family: 'Fira Code', monospace;
1958+
border: 1px dashed rgba(0, 229, 255, 0.2);
1959+
background: rgba(0, 0, 0, 0.3);
1960+
margin-top: 1rem;
1961+
}

0 commit comments

Comments
 (0)