Skip to content

Commit 249f69e

Browse files
author
Mark Saroufim
committed
Categorize homepage leaderboards into Active, Getting Started, and Closed sections
Split the flat leaderboard grid into three sections to help users find relevant competitions. Beginner problems (pmpp) get a dedicated "Getting Started" section, closed competitions move to the bottom with an "Ended" chip on each tile.
1 parent 87c126b commit 249f69e

2 files changed

Lines changed: 86 additions & 12 deletions

File tree

frontend/src/pages/home/Home.tsx

Lines changed: 66 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ interface LeaderboardSummaries {
4747
now: string;
4848
}
4949

50+
function isBeginnerProblem(name: string): boolean {
51+
return /pmpp/i.test(name);
52+
}
53+
5054
export default function Home() {
5155
const [searchParams] = useSearchParams();
5256
const navigate = useNavigate();
@@ -73,6 +77,16 @@ export default function Home() {
7377
(lb) => !isExpired(lb.deadline)
7478
);
7579

80+
const activeCompetitions = leaderboards.filter(
81+
(lb) => !isExpired(lb.deadline) && !isBeginnerProblem(lb.name)
82+
);
83+
const beginnerProblems = leaderboards.filter(
84+
(lb) => !isExpired(lb.deadline) && isBeginnerProblem(lb.name)
85+
);
86+
const closedCompetitions = leaderboards.filter((lb) =>
87+
isExpired(lb.deadline)
88+
);
89+
7690
const handleLeaderboardSelect = (id: number) => {
7791
setIsLeaderboardSelectOpen(false);
7892
navigate(`/leaderboard/${id}/editor`);
@@ -215,13 +229,58 @@ export default function Home() {
215229
) : loading ? (
216230
<Loading />
217231
) : leaderboards.length > 0 ? (
218-
<Grid container spacing={3}>
219-
{leaderboards.map((leaderboard) => (
220-
<Grid size={{ xs: 12, sm: 6, md: 4, lg: 4 }} key={leaderboard.id}>
221-
<LeaderboardTile leaderboard={leaderboard} />
222-
</Grid>
223-
))}
224-
</Grid>
232+
<Box>
233+
{/* Active Competitions */}
234+
{activeCompetitions.length > 0 && (
235+
<Box sx={{ mb: 5 }}>
236+
<Typography variant="h5" component="h2" sx={{ mb: 2 }}>
237+
Active Competitions
238+
</Typography>
239+
<Grid container spacing={3}>
240+
{activeCompetitions.map((leaderboard) => (
241+
<Grid size={{ xs: 12, sm: 6, md: 4, lg: 4 }} key={leaderboard.id}>
242+
<LeaderboardTile leaderboard={leaderboard} />
243+
</Grid>
244+
))}
245+
</Grid>
246+
</Box>
247+
)}
248+
249+
{/* Getting Started */}
250+
{beginnerProblems.length > 0 && (
251+
<Box sx={{ mb: 5 }}>
252+
<Typography variant="h5" component="h2" sx={{ mb: 0.5 }}>
253+
Getting Started
254+
</Typography>
255+
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
256+
New to GPU programming? Start here.
257+
</Typography>
258+
<Grid container spacing={3}>
259+
{beginnerProblems.map((leaderboard) => (
260+
<Grid size={{ xs: 12, sm: 6, md: 4, lg: 4 }} key={leaderboard.id}>
261+
<LeaderboardTile leaderboard={leaderboard} />
262+
</Grid>
263+
))}
264+
</Grid>
265+
</Box>
266+
)}
267+
268+
{/* Closed Competitions */}
269+
{closedCompetitions.length > 0 && (
270+
<Box>
271+
<Typography variant="h5" component="h2" sx={{ mb: 2, color: "text.secondary" }}>
272+
Closed Competitions
273+
</Typography>
274+
<Grid container spacing={3}>
275+
{closedCompetitions.map((leaderboard) => (
276+
<Grid size={{ xs: 12, sm: 6, md: 4, lg: 4 }} key={leaderboard.id}>
277+
<LeaderboardTile leaderboard={leaderboard} expired />
278+
</Grid>
279+
))}
280+
</Grid>
281+
</Box>
282+
)}
283+
</Box>
225284
) : (
226285
<Typography variant="body1" color="text.secondary">
227286
No active leaderboards found.

frontend/src/pages/home/components/LeaderboardTile.tsx

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,10 @@ interface LeaderboardData {
6363

6464
interface LeaderboardTileProps {
6565
leaderboard: LeaderboardData;
66+
expired?: boolean;
6667
}
6768

68-
export default function LeaderboardTile({ leaderboard }: LeaderboardTileProps) {
69+
export default function LeaderboardTile({ leaderboard, expired }: LeaderboardTileProps) {
6970
const timeLeft = getTimeLeft(leaderboard.deadline);
7071

7172
return (
@@ -78,7 +79,7 @@ export default function LeaderboardTile({ leaderboard }: LeaderboardTileProps) {
7879
sx={{ flexGrow: 1, display: "flex", flexDirection: "column" }}
7980
>
8081
{/* Leaderboard Name */}
81-
<Box sx={{ mb: 1 }}>
82+
<Box sx={{ mb: 1, display: "flex", alignItems: "center", gap: 1 }}>
8283
<Typography
8384
variant="h6"
8485
component="h3"
@@ -87,12 +88,26 @@ export default function LeaderboardTile({ leaderboard }: LeaderboardTileProps) {
8788
<ColoredSquare name={leaderboard.name} />
8889
{leaderboard.name}
8990
</Typography>
91+
{expired && (
92+
<Chip
93+
label="Ended"
94+
size="small"
95+
sx={{
96+
fontSize: "0.7rem",
97+
height: 22,
98+
bgcolor: "action.disabledBackground",
99+
color: "text.secondary",
100+
}}
101+
/>
102+
)}
90103
</Box>
91104

92105
{/* Time Left */}
93-
<Typography variant="body1" sx={{ color: "text.secondary" }}>
94-
{timeLeft}
95-
</Typography>
106+
{!expired && (
107+
<Typography variant="body1" sx={{ color: "text.secondary" }}>
108+
{timeLeft}
109+
</Typography>
110+
)}
96111

97112
{/* GPU Types */}
98113
<Typography

0 commit comments

Comments
 (0)