rafmacalaba commited on
Commit
542e5d3
Β·
1 Parent(s): e746bfe

feat: add refresh button to leaderboard + cache-bust API calls

Browse files
Files changed (2) hide show
  1. app/components/Leaderboard.js +25 -10
  2. app/globals.css +31 -0
app/components/Leaderboard.js CHANGED
@@ -1,6 +1,6 @@
1
  "use client";
2
 
3
- import { useState, useEffect } from 'react';
4
 
5
  const MEDALS = ['πŸ₯‡', 'πŸ₯ˆ', 'πŸ₯‰'];
6
 
@@ -8,15 +8,20 @@ export default function Leaderboard({ isOpen, onClose }) {
8
  const [data, setData] = useState(null);
9
  const [loading, setLoading] = useState(false);
10
 
 
 
 
 
 
 
 
 
 
11
  useEffect(() => {
12
  if (isOpen && !data) {
13
- setLoading(true);
14
- fetch('/api/leaderboard')
15
- .then(res => res.json())
16
- .then(d => { setData(d); setLoading(false); })
17
- .catch(() => setLoading(false));
18
  }
19
- }, [isOpen, data]);
20
 
21
  if (!isOpen) return null;
22
 
@@ -26,11 +31,21 @@ export default function Leaderboard({ isOpen, onClose }) {
26
  <div className="leaderboard-modal">
27
  <div className="leaderboard-header">
28
  <h3>πŸ† Annotation Leaderboard</h3>
29
- <button className="panel-close" onClick={onClose}>&times;</button>
 
 
 
 
 
 
 
 
 
 
30
  </div>
31
 
32
  <div className="leaderboard-body">
33
- {loading ? (
34
  <div className="leaderboard-loading">
35
  <div className="spinner" />
36
  <p>Tallying scores...</p>
@@ -75,7 +90,7 @@ export default function Leaderboard({ isOpen, onClose }) {
75
  </div>
76
 
77
  <div className="leaderboard-footer">
78
- <p>Score = Verified + Added β€’ Updates every 2 min</p>
79
  </div>
80
  </div>
81
  </>
 
1
  "use client";
2
 
3
+ import { useState, useEffect, useCallback } from 'react';
4
 
5
  const MEDALS = ['πŸ₯‡', 'πŸ₯ˆ', 'πŸ₯‰'];
6
 
 
8
  const [data, setData] = useState(null);
9
  const [loading, setLoading] = useState(false);
10
 
11
+ const fetchLeaderboard = useCallback(() => {
12
+ setLoading(true);
13
+ fetch(`/api/leaderboard?t=${Date.now()}`) // cache-bust
14
+ .then(res => res.json())
15
+ .then(d => { setData(d); setLoading(false); })
16
+ .catch(() => setLoading(false));
17
+ }, []);
18
+
19
+ // Fetch on first open
20
  useEffect(() => {
21
  if (isOpen && !data) {
22
+ fetchLeaderboard();
 
 
 
 
23
  }
24
+ }, [isOpen, data, fetchLeaderboard]);
25
 
26
  if (!isOpen) return null;
27
 
 
31
  <div className="leaderboard-modal">
32
  <div className="leaderboard-header">
33
  <h3>πŸ† Annotation Leaderboard</h3>
34
+ <div className="leaderboard-header-actions">
35
+ <button
36
+ className="btn-refresh"
37
+ onClick={fetchLeaderboard}
38
+ disabled={loading}
39
+ title="Refresh"
40
+ >
41
+ {loading ? '⏳' : 'πŸ”„'}
42
+ </button>
43
+ <button className="panel-close" onClick={onClose}>&times;</button>
44
+ </div>
45
  </div>
46
 
47
  <div className="leaderboard-body">
48
+ {loading && !data ? (
49
  <div className="leaderboard-loading">
50
  <div className="spinner" />
51
  <p>Tallying scores...</p>
 
90
  </div>
91
 
92
  <div className="leaderboard-footer">
93
+ <p>Score = Verified + Added</p>
94
  </div>
95
  </div>
96
  </>
app/globals.css CHANGED
@@ -265,6 +265,37 @@ h4 {
265
  font-size: 1rem;
266
  }
267
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
  .leaderboard-body {
269
  padding: 12px 20px;
270
  overflow-y: auto;
 
265
  font-size: 1rem;
266
  }
267
 
268
+ .leaderboard-header-actions {
269
+ display: flex;
270
+ align-items: center;
271
+ gap: 8px;
272
+ }
273
+
274
+ .btn-refresh {
275
+ background: none;
276
+ border: 1px solid var(--border-color);
277
+ color: var(--text-color);
278
+ width: 28px;
279
+ height: 28px;
280
+ border-radius: 6px;
281
+ cursor: pointer;
282
+ font-size: 0.8rem;
283
+ display: flex;
284
+ align-items: center;
285
+ justify-content: center;
286
+ transition: all 0.2s;
287
+ }
288
+
289
+ .btn-refresh:hover:not(:disabled) {
290
+ background: var(--surface);
291
+ border-color: var(--accent);
292
+ }
293
+
294
+ .btn-refresh:disabled {
295
+ opacity: 0.5;
296
+ cursor: wait;
297
+ }
298
+
299
  .leaderboard-body {
300
  padding: 12px 20px;
301
  overflow-y: auto;