Skip to content

Commit 82efdb1

Browse files
authored
Feature/leaderboard style update (#192)
1 parent ed9b560 commit 82efdb1

3 files changed

Lines changed: 34 additions & 73 deletions

File tree

frontend/components/leaderboard/LeaderboardClient.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ export default function LeaderboardClient({
2525
return (
2626
<div className="relative min-h-screen w-full">
2727
<div className="fixed inset-0 z-0">
28-
<DynamicGridBackground className="w-full h-full bg-gray-50 transition-colors duration-300 dark:bg-transparent" />
28+
<DynamicGridBackground
29+
showStaticGrid
30+
className="w-full h-full bg-gray-50 transition-colors duration-300 dark:bg-transparent"
31+
/>
2932
</div>
3033

3134
<div className="relative z-10 w-full max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 flex flex-col items-center pt-20 pb-10">

frontend/components/leaderboard/LeaderboardPodium.tsx

Lines changed: 5 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ const rankConfig = {
1616
text: 'text-yellow-600 dark:text-yellow-400',
1717
badge: 'bg-yellow-400 dark:bg-yellow-500',
1818
ring: 'border-yellow-400 dark:border-yellow-500',
19-
barTop: 'bg-yellow-400 dark:bg-yellow-500',
2019
},
2120
},
2221
2: {
@@ -28,7 +27,6 @@ const rankConfig = {
2827
text: 'text-slate-600 dark:text-slate-400',
2928
badge: 'bg-slate-400 dark:bg-slate-500',
3029
ring: 'border-slate-300 dark:border-slate-500',
31-
barTop: 'bg-slate-300 dark:bg-slate-500',
3230
},
3331
},
3432
3: {
@@ -40,7 +38,6 @@ const rankConfig = {
4038
text: 'text-orange-600 dark:text-orange-400',
4139
badge: 'bg-orange-400 dark:bg-orange-500',
4240
ring: 'border-orange-300 dark:border-orange-500',
43-
barTop: 'bg-orange-300 dark:bg-orange-500',
4441
},
4542
},
4643
} as const;
@@ -55,40 +52,11 @@ export function LeaderboardPodium({ topThree }: { topThree: User[] }) {
5552
return (
5653
<div className="flex items-end justify-center gap-4 md:gap-8 h-[350px] w-full max-w-3xl mx-auto">
5754
{podiumOrder.map(user => {
58-
const rank = user.rank;
55+
const rank = user.rank as 1 | 2 | 3;
5956
const isFirst = rank === 1;
6057

61-
const height = rank === 1 ? '100%' : rank === 2 ? '40%' : '35%';
62-
const delay = rank === 1 ? 0.4 : rank === 2 ? 0.2 : 0.6;
63-
64-
const rankStyles = {
65-
1: {
66-
border: 'border-yellow-400 dark:border-yellow-500',
67-
bg: 'bg-yellow-400/20 dark:bg-yellow-500/10',
68-
text: 'text-yellow-600 dark:text-yellow-400',
69-
badge: 'bg-yellow-400 dark:bg-yellow-500',
70-
ring: 'border-yellow-400 dark:border-yellow-500',
71-
barTop: 'bg-yellow-400 dark:bg-yellow-500',
72-
},
73-
2: {
74-
border: 'border-slate-300 dark:border-slate-500',
75-
bg: 'bg-slate-300/20 dark:bg-slate-500/10',
76-
text: 'text-slate-600 dark:text-slate-400',
77-
badge: 'bg-slate-400 dark:bg-slate-500',
78-
ring: 'border-slate-300 dark:border-slate-500',
79-
barTop: 'bg-slate-300 dark:bg-slate-500',
80-
},
81-
3: {
82-
border: 'border-orange-300 dark:border-orange-500',
83-
bg: 'bg-orange-300/20 dark:bg-orange-500/10',
84-
text: 'text-orange-600 dark:text-orange-400',
85-
badge: 'bg-orange-400 dark:bg-orange-500',
86-
ring: 'border-orange-300 dark:border-orange-500',
87-
barTop: 'bg-orange-300 dark:bg-orange-500',
88-
},
89-
};
90-
91-
const style = rankStyles[rank as 1 | 2 | 3] || rankStyles[2];
58+
const config = rankConfig[rank] || rankConfig[2];
59+
const { height, delay, style } = config;
9260

9361
return (
9462
<div
@@ -141,7 +109,7 @@ export function LeaderboardPodium({ topThree }: { topThree: User[] }) {
141109

142110
<div
143111
className={cn(
144-
'font-mono text-[10px] md:text-xs font-bold mt-0.5',
112+
'font-mono font-bold mt-0.5 text-base md:text-lg',
145113
style.text
146114
)}
147115
>
@@ -164,14 +132,7 @@ export function LeaderboardPodium({ topThree }: { topThree: User[] }) {
164132
style.bg,
165133
style.border
166134
)}
167-
>
168-
<div
169-
className={cn(
170-
'w-full h-1 md:h-1.5 absolute top-0 left-0 opacity-80',
171-
style.barTop
172-
)}
173-
/>
174-
</motion.div>
135+
></motion.div>
175136
</div>
176137
);
177138
})}

frontend/components/leaderboard/LeaderboardTable.tsx

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -34,25 +34,23 @@ export function LeaderboardTable({
3434
<div className="w-full max-w-4xl flex flex-col gap-4">
3535
<div className="bg-white dark:bg-white/5 backdrop-blur-md rounded-2xl border border-slate-200 dark:border-white/10 overflow-hidden shadow-lg dark:shadow-2xl">
3636
<div className="overflow-x-auto w-full">
37-
{/* ✅ ВИПРАВЛЕННЯ 1: border-separate + border-spacing-0 замість border-collapse */}
38-
<table className="w-full min-w-[350px] text-left border-separate border-spacing-0">
37+
<table className="w-full text-left border-separate border-spacing-0">
3938
<caption className="sr-only">{t('tableCaption')}</caption>
4039

4140
<thead className="bg-slate-50/80 dark:bg-white/5">
4241
<tr>
43-
<th className="px-3 sm:px-6 py-4 sm:py-5 text-center text-xs font-bold text-slate-500 dark:text-slate-400 uppercase tracking-widest w-[15%] border-b border-slate-200 dark:border-white/10">
42+
<th className="px-2 sm:px-6 py-3 sm:py-5 text-center text-[10px] sm:text-xs font-bold text-slate-500 dark:text-slate-400 uppercase tracking-widest w-12 sm:w-[15%] border-b border-slate-200 dark:border-white/10">
4443
{t('rank')}
4544
</th>
46-
<th className="px-3 sm:px-6 py-4 sm:py-5 text-xs font-bold text-slate-500 dark:text-slate-400 uppercase tracking-widest w-[60%] border-b border-slate-200 dark:border-white/10">
45+
<th className="px-2 sm:px-6 py-3 sm:py-5 text-xs font-bold text-slate-500 dark:text-slate-400 uppercase tracking-widest w-auto border-b border-slate-200 dark:border-white/10">
4746
{t('user')}
4847
</th>
49-
<th className="px-3 sm:px-6 py-4 sm:py-5 text-right text-xs font-bold text-slate-500 dark:text-slate-400 uppercase tracking-widest w-[25%] border-b border-slate-200 dark:border-white/10">
48+
<th className="px-2 sm:px-6 py-3 sm:py-5 text-right text-xs font-bold text-slate-500 dark:text-slate-400 uppercase tracking-widest w-[25%] sm:w-[20%] border-b border-slate-200 dark:border-white/10">
5049
{t('score')}
5150
</th>
5251
</tr>
5352
</thead>
5453

55-
{/* ✅ ВИПРАВЛЕННЯ 2: Прибрали divide-y, бо він конфліктує з border-separate */}
5654
<tbody>
5755
{topUsers.map(user => {
5856
const isMe =
@@ -81,7 +79,7 @@ export function LeaderboardTable({
8179

8280
<div className="bg-white dark:bg-white/5 backdrop-blur-md rounded-2xl border-2 border-[var(--accent-primary)] overflow-hidden shadow-[0_0_20px_var(--accent-primary)]">
8381
<div className="overflow-x-auto w-full">
84-
<table className="w-full min-w-[350px] text-left border-separate border-spacing-0">
82+
<table className="w-full text-left border-separate border-spacing-0">
8583
<tbody>
8684
<TableRow user={matchedUser} isCurrentUser={true} t={t} />
8785
</tbody>
@@ -103,20 +101,23 @@ function TableRow({
103101
isCurrentUser: boolean;
104102
t: ReturnType<typeof useTranslations>;
105103
}) {
106-
// ✅ ВИПРАВЛЕННЯ 3: Спільний клас для ліній між рядками
107104
const cellClass =
108-
'px-3 sm:px-6 py-4 border-b border-slate-100 dark:border-white/5';
105+
'px-2 sm:px-6 py-3 sm:py-4 border-b border-slate-100 dark:border-white/5';
106+
107+
const leftBorderClass = isCurrentUser
108+
? 'border-l-[1px] sm:border-l-[1px] border-l-[var(--accent-primary)]'
109+
: 'border-l-[1px] sm:border-l-[1px] border-l-transparent';
109110

110111
return (
111112
<tr
112113
className={cn(
113114
'group transition-all duration-300',
114115
isCurrentUser
115-
? 'bg-[color-mix(in_srgb,var(--accent-primary),transparent_90%)] border-l-[4px] sm:border-l-[6px] border-l-[var(--accent-primary)] shadow-inner'
116-
: 'hover:bg-slate-50 dark:hover:bg-white/5 border-l-[4px] sm:border-l-[6px] border-l-transparent'
116+
? 'bg-[color-mix(in_srgb,var(--accent-primary),transparent_90%)] shadow-inner'
117+
: 'hover:bg-slate-50/60 dark:hover:bg-white/[0.04]'
117118
)}
118119
>
119-
<td className={cellClass}>
120+
<td className={cn(cellClass, leftBorderClass)}>
120121
<div className="flex justify-center items-center">
121122
<RankBadge rank={user.rank} />
122123
</div>
@@ -139,18 +140,18 @@ function TableRow({
139140
<div className="flex flex-col min-w-0">
140141
<span
141142
className={cn(
142-
'font-medium text-sm transition-colors flex items-center gap-2 truncate',
143+
'font-medium text-sm transition-colors flex items-center gap-1 sm:gap-2 truncate',
143144
isCurrentUser
144145
? 'text-[var(--accent-primary)] font-black text-sm sm:text-base'
145-
: 'text-slate-700 dark:text-slate-200 group-hover:text-[var(--accent-primary)]'
146+
: 'text-slate-700 dark:text-slate-200 group-hover:text-[var(--accent-primary)] dark:group-hover:text-[var(--accent-primary)]'
146147
)}
147148
>
148-
<span className="truncate max-w-[120px] sm:max-w-none">
149+
<span className="truncate max-w-[100px] sm:max-w-none">
149150
{user.username}
150151
</span>
151152

152153
{isCurrentUser && (
153-
<div className="relative flex-shrink-0 flex items-center justify-center w-6 h-6 sm:w-8 sm:h-8 ml-1">
154+
<div className="relative flex-shrink-0 flex items-center justify-center w-5 h-5 sm:w-8 sm:h-8 ml-1">
154155
<motion.div
155156
animate={{ scale: [1, 1.2, 1] }}
156157
transition={{
@@ -168,10 +169,6 @@ function TableRow({
168169
<path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z" />
169170
</svg>
170171
</motion.div>
171-
172-
<span className="relative z-10 text-[6px] sm:text-[8px] font-black text-white uppercase tracking-tighter -mt-0.5">
173-
{t('you') || 'YOU'}
174-
</span>
175172
</div>
176173
)}
177174
</span>
@@ -191,8 +188,8 @@ function TableRow({
191188
className={cn(
192189
'font-mono font-bold inline-block transition-all',
193190
isCurrentUser
194-
? 'text-[var(--accent-primary)] scale-110 drop-shadow-sm text-base sm:text-lg'
195-
: 'text-slate-700 dark:text-slate-300 group-hover:scale-105'
191+
? 'text-[var(--accent-primary)] scale-110 drop-shadow-sm text-sm sm:text-lg'
192+
: 'text-slate-700 dark:text-slate-300 group-hover:scale-105 text-sm sm:text-base'
196193
)}
197194
>
198195
{user.points.toLocaleString()}
@@ -205,32 +202,32 @@ function TableRow({
205202
function RankBadge({ rank }: { rank: number }) {
206203
if (rank === 1) {
207204
return (
208-
<div className="relative w-10 h-6 sm:w-14 sm:h-8 flex items-center justify-center gap-1 sm:gap-1.5 rounded-lg bg-yellow-100 dark:bg-yellow-500/20 border border-yellow-500/50 shadow-[0_0_10px_rgba(234,179,8,0.3)]">
205+
<div className="relative w-8 h-6 sm:w-14 sm:h-8 flex items-center justify-center gap-0.5 sm:gap-1.5 rounded-md sm:rounded-lg bg-yellow-100 dark:bg-yellow-500/20 border border-yellow-500/50 shadow-[0_0_10px_rgba(234,179,8,0.3)]">
209206
<span className="font-black text-xs sm:text-base text-yellow-700 dark:text-yellow-400">
210207
1
211208
</span>
212-
<Trophy className="w-3 h-3 sm:w-4 sm:h-4 text-yellow-600 dark:text-yellow-400" />
209+
<Trophy className="w-2.5 h-2.5 sm:w-4 sm:h-4 text-yellow-600 dark:text-yellow-400" />
213210
<div className="absolute -top-1 -right-1 w-1.5 h-1.5 sm:w-2 sm:h-2 bg-yellow-400 rounded-full animate-pulse" />
214211
</div>
215212
);
216213
}
217214
if (rank === 2) {
218215
return (
219-
<div className="w-10 h-6 sm:w-14 sm:h-8 flex items-center justify-center gap-1 sm:gap-1.5 rounded-lg bg-slate-100 dark:bg-slate-400/10 border border-slate-300 dark:border-slate-400/30">
216+
<div className="w-8 h-6 sm:w-14 sm:h-8 flex items-center justify-center gap-0.5 sm:gap-1.5 rounded-md sm:rounded-lg bg-slate-100 dark:bg-slate-400/10 border border-slate-300 dark:border-slate-400/30">
220217
<span className="font-black text-xs sm:text-base text-slate-600 dark:text-slate-300">
221218
2
222219
</span>
223-
<Medal className="w-3 h-3 sm:w-4 sm:h-4 text-slate-500 dark:text-slate-300" />
220+
<Medal className="w-2.5 h-2.5 sm:w-4 sm:h-4 text-slate-500 dark:text-slate-300" />
224221
</div>
225222
);
226223
}
227224
if (rank === 3) {
228225
return (
229-
<div className="w-10 h-6 sm:w-14 sm:h-8 flex items-center justify-center gap-1 sm:gap-1.5 rounded-lg bg-orange-50 dark:bg-orange-500/10 border border-orange-300 dark:border-orange-500/30">
226+
<div className="w-8 h-6 sm:w-14 sm:h-8 flex items-center justify-center gap-0.5 sm:gap-1.5 rounded-md sm:rounded-lg bg-orange-50 dark:bg-orange-500/10 border border-orange-300 dark:border-orange-500/30">
230227
<span className="font-black text-xs sm:text-base text-orange-700 dark:text-orange-400">
231228
3
232229
</span>
233-
<Medal className="w-3 h-3 sm:w-4 sm:h-4 text-orange-600 dark:text-orange-400" />
230+
<Medal className="w-2.5 h-2.5 sm:w-4 sm:h-4 text-orange-600 dark:text-orange-400" />
234231
</div>
235232
);
236233
}

0 commit comments

Comments
 (0)