@@ -9,11 +9,13 @@ import {
99 Info ,
1010 AlertCircle ,
1111 Lightbulb ,
12+ Trophy ,
1213} from "lucide-react" ;
1314import ProfileHeader from "../components/ProfileHeader" ;
1415import { useAuth } from "../context/AuthContext" ;
1516import POTDStreakHeatmap from "../components/POTDStreakHeatmap" ;
1617import LoadingIndicator from "../components/LoadingIndicator" ;
18+ import type { User } from "../types/User" ;
1719
1820export const Route = createFileRoute ( "/potd" ) ( {
1921 component : RouteComponent ,
@@ -28,21 +30,33 @@ interface Problem {
2830 tags : string [ ] ;
2931}
3032
33+ interface TopSolver {
34+ user : User ;
35+ solveCount : number ;
36+ }
37+
3138export function RouteComponent ( ) {
3239 const [ problem , setProblem ] = useState < Problem | null > ( null ) ;
3340 const [ solved , setSolved ] = useState ( false ) ;
41+ const [ solveCount , setSolveCount ] = useState < number > ( 0 ) ;
3442 const [ showTags , setShowTags ] = useState ( false ) ;
3543 const [ checking , setChecking ] = useState ( false ) ;
3644 const [ message , setMessage ] = useState < string | null > ( null ) ;
45+ const [ showTop , setShowTop ] = useState ( true ) ;
46+ const [ topSolver , setTopSolver ] = useState < TopSolver | null > ( null ) ;
47+ const [ loadingTop , setLoadingTop ] = useState ( false ) ;
3748
3849 const { user } = useAuth ( ) ;
3950
4051 useEffect ( ( ) => {
52+ fetchTopSolver ( ) ;
53+
4154 fetch ( `${ import . meta. env . VITE_API_BASE_URL } /potd/current` )
4255 . then ( ( res ) => res . json ( ) )
4356 . then ( ( data ) => {
4457 setProblem ( data . problem ) ;
4558 setSolved ( data . solved ) ;
59+ setSolveCount ( data . solveCount ) ;
4660 } )
4761 . catch ( ( ) => setProblem ( null ) ) ;
4862 } , [ ] ) ;
@@ -67,6 +81,7 @@ export function RouteComponent() {
6781 setMessage ( json . message ) ;
6882 if ( res . ok ) {
6983 setSolved ( true ) ;
84+ fetchTopSolver ( ) ;
7085 }
7186 } catch {
7287 setMessage ( "Error checking solve" ) ;
@@ -75,6 +90,27 @@ export function RouteComponent() {
7590 }
7691 } ;
7792
93+ const fetchTopSolver = async ( ) => {
94+ setLoadingTop ( true ) ;
95+ try {
96+ const res = await fetch (
97+ `${ import . meta. env . VITE_API_BASE_URL } /potd/stats`
98+ ) ;
99+ const { topSolvers } = await res . json ( ) ;
100+ if ( topSolvers . length ) setTopSolver ( topSolvers [ 0 ] ) ;
101+ } catch {
102+ // ignore
103+ } finally {
104+ setLoadingTop ( false ) ;
105+ }
106+ } ;
107+
108+ const toggleTop = ( ) => {
109+ const next = ! showTop ;
110+ setShowTop ( next ) ;
111+ if ( next && ! topSolver ) fetchTopSolver ( ) ;
112+ } ;
113+
78114 if ( ! problem ) {
79115 return < LoadingIndicator /> ;
80116 }
@@ -101,12 +137,15 @@ export function RouteComponent() {
101137 < div className = "mx-auto" >
102138 < div className = "flex flex-col lg:flex-row gap-6" >
103139 { /* POTD Card */ }
104- < div className = "flex-1 bg-highlight-dark rounded-lg p-4 lg:p-6" >
140+ < div className = "flex-1 bg-highlight-dark rounded-lg p-4 lg:p-6 flex flex-col justify-between " >
105141 < div className = "flex items-start justify-between mb-4" >
106142 < div >
107143 < h2 className = "text-3xl text-white mb-1" > { problem . name } </ h2 >
108144 < div className = "text-muted text-sm" >
109- Contest { problem . contestId } • Problem { problem . index }
145+ Contest { problem . contestId } • Problem { problem . index } • Solved
146+ by < span className = "text-white" > { solveCount } </ span > { " " }
147+ { solveCount == 1 ? "person " : "people " }
148+ today
110149 </ div >
111150 </ div >
112151 < button
@@ -194,23 +233,64 @@ export function RouteComponent() {
194233
195234 { /* How it works */ }
196235 < div className = "lg:w-1/3 bg-highlight-dark rounded-lg p-4 lg:p-6" >
197- < h3 className = "text-lg text-white mb-3 flex items-center gap-1" >
198- < Lightbulb className = "w-4 h-4" /> How it works
199- </ h3 >
200- < div className = "space-y-2 text-muted text-xs" >
201- < div className = "flex items-start gap-2" >
202- < span className = "text-accent-purple" > 1.</ span >
203- < span > Click "Solve" to open the problem</ span >
204- </ div >
205- < div className = "flex items-start gap-2" >
206- < span className = "text-accent-purple" > 2.</ span >
207- < span > Submit your solution on Codeforces</ span >
208- </ div >
209- < div className = "flex items-start gap-2" >
210- < span className = "text-accent-purple" > 3.</ span >
211- < span > Return and click "Verify" to check your answer</ span >
212- </ div >
236+ < div className = "flex items-center justify-between mb-3" >
237+ < h3 className = "text-lg text-white flex items-center gap-1" >
238+ { showTop ? (
239+ < Trophy className = "w-5 h-5 mr-2 text-accent-yellow" />
240+ ) : (
241+ < Lightbulb className = "w-5 h-5 " />
242+ ) } { " " }
243+ { showTop ? "Hall of Fame" : "How it works" }
244+ </ h3 >
245+ < button
246+ onClick = { toggleTop }
247+ className = "text-xs text-accent-yellow hover:underline hover:cursor-pointer"
248+ >
249+ { showTop ? "How to solve?" : "See Top" }
250+ </ button >
213251 </ div >
252+
253+ { showTop ? (
254+ loadingTop ? (
255+ < LoadingIndicator />
256+ ) : topSolver ? (
257+ < div className = "p-2 flex flex-col items-center gap-3 max-w-xs mx-auto" >
258+ < img
259+ src = { topSolver . user . pfpUrl }
260+ className = "w-12 h-12 rounded-full"
261+ />
262+ < div className = "flex items-center gap-1" >
263+ < h4 className = "text-lg font-semibold text-white" >
264+ { topSolver . user . name }
265+ </ h4 >
266+ </ div >
267+ < p className = "text-base text-muted" >
268+ has solved{ " " }
269+ < span className = "font-bold text-accent-yellow" >
270+ { topSolver . solveCount }
271+ </ span > { " " }
272+ POTD{ topSolver . solveCount > 1 ? "s" : "" }
273+ </ p >
274+ </ div >
275+ ) : (
276+ < div className = "text-muted text-sm" > No solves yet.</ div >
277+ )
278+ ) : (
279+ < div className = "space-y-2 text-muted text-xs" >
280+ < div className = "flex items-start gap-2" >
281+ < span className = "text-accent-purple" > 1.</ span >
282+ < span > Click "Solve" to open the problem</ span >
283+ </ div >
284+ < div className = "flex items-start gap-2" >
285+ < span className = "text-accent-purple" > 2.</ span >
286+ < span > Submit your solution on Codeforces</ span >
287+ </ div >
288+ < div className = "flex items-start gap-2" >
289+ < span className = "text-accent-purple" > 3.</ span >
290+ < span > Return and click "Verify" to check your answer</ span >
291+ </ div >
292+ </ div >
293+ ) }
214294 </ div >
215295 </ div >
216296 </ div >
0 commit comments