Skip to content

Commit fd877c9

Browse files
committed
feat: implement country leaderboard client and enhance leaderboard page with hero section
1 parent 8067e4f commit fd877c9

8 files changed

Lines changed: 424 additions & 291 deletions

File tree

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
"use client";
2+
3+
import { useState } from "react";
4+
import Link from "next/link";
5+
import { ArrowLeft, Loader2 } from "lucide-react";
6+
import { LeaderboardTable } from "@/components/leaderboard-table";
7+
import { AppHeader } from "@/components/app-header";
8+
import { AppFooter } from "@/components/app-footer";
9+
import { Button } from "@/components/ui/button";
10+
import { useTranslation } from "@/components/language-provider";
11+
import type { LeaderboardResult } from "@/lib/leaderboard";
12+
13+
type Props = {
14+
countryTitle: string;
15+
initialLeaderboard: LeaderboardResult;
16+
initialError?: string | null;
17+
};
18+
19+
export function CountryLeaderboardClient({
20+
countryTitle,
21+
initialLeaderboard,
22+
initialError = null,
23+
}: Props) {
24+
const { t } = useTranslation();
25+
const [title] = useState(initialLeaderboard.title || countryTitle);
26+
const [totalFromSource] = useState(initialLeaderboard.totalFromSource);
27+
const [scored] = useState(initialLeaderboard.scored);
28+
const [errors] = useState(initialLeaderboard.errors);
29+
const [failed] = useState<string | null>(initialError);
30+
const [loading] = useState(false);
31+
32+
if (failed) {
33+
return (
34+
<main className="flex min-h-screen flex-col">
35+
<AppHeader />
36+
<div className="mx-auto w-full max-w-6xl flex-1 px-4 py-10">
37+
<div className="mx-auto flex max-w-2xl flex-col items-center justify-center gap-5 rounded-3xl border border-destructive/25 bg-gradient-to-b from-destructive/10 via-destructive/5 to-background px-6 py-12 text-center shadow-sm">
38+
<p className="text-lg font-semibold tracking-tight text-foreground">
39+
{t("leaderboard.error.title")}
40+
</p>
41+
<p className="max-w-xl text-sm leading-7 text-muted-foreground">
42+
{failed}
43+
</p>
44+
<Link href="/leaderboard">
45+
<Button variant="ghost">{t("leaderboard.back")}</Button>
46+
</Link>
47+
</div>
48+
</div>
49+
<AppFooter />
50+
</main>
51+
);
52+
}
53+
54+
return (
55+
<main className="flex min-h-screen flex-col">
56+
<AppHeader />
57+
<div className="mx-auto flex w-full max-w-6xl flex-1 flex-col gap-6 px-4 py-10">
58+
<div className="flex items-center gap-3">
59+
<Link href="/leaderboard">
60+
<Button variant="ghost" size="sm">
61+
<ArrowLeft className="mr-1 h-4 w-4 rtl:-scale-x-100" />
62+
{t("leaderboard.back")}
63+
</Button>
64+
</Link>
65+
</div>
66+
67+
<section className="rounded-3xl border border-border/60 bg-gradient-to-br from-primary/10 via-background to-background px-6 py-8 shadow-sm">
68+
<div className="max-w-3xl space-y-3">
69+
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-primary/80">
70+
{t("leaderboard.header.eyebrow")}
71+
</p>
72+
<h1 className="text-3xl font-semibold tracking-tight text-foreground sm:text-4xl">
73+
{t("leaderboard.country.title", { title })}
74+
</h1>
75+
<p className="text-sm leading-7 text-muted-foreground sm:text-base">
76+
{t("leaderboard.country.description", { title })}
77+
</p>
78+
</div>
79+
</section>
80+
81+
{scored.length > 0 ? (
82+
<div className="animate-fadeIn">
83+
<LeaderboardTable
84+
users={scored}
85+
failedUsers={errors}
86+
title={title}
87+
totalFromSource={totalFromSource}
88+
usersProcessed={scored.length}
89+
/>
90+
</div>
91+
) : loading ? (
92+
<div className="flex items-center justify-center gap-2 py-20">
93+
<Loader2 className="h-5 w-5 animate-spin text-primary" />
94+
<span className="text-sm text-muted-foreground">
95+
{t("leaderboard.loading")}
96+
</span>
97+
</div>
98+
) : (
99+
<div className="flex flex-col items-center gap-4 py-20 text-center">
100+
<p className="text-lg font-medium text-muted-foreground">
101+
{t("leaderboard.noDevelopersFor", { title })}
102+
</p>
103+
<Link href="/leaderboard">
104+
<Button variant="secondary">{t("leaderboard.back")}</Button>
105+
</Link>
106+
</div>
107+
)}
108+
</div>
109+
<AppFooter />
110+
</main>
111+
);
112+
}

0 commit comments

Comments
 (0)