leaderboard column colours changed
This commit is contained in:
@@ -16,47 +16,85 @@ const rankBadge = (i) =>
|
|||||||
i === 2 ? 'bg-orange-100 text-orange-700 dark:bg-orange-500/20 dark:text-orange-300' :
|
i === 2 ? 'bg-orange-100 text-orange-700 dark:bg-orange-500/20 dark:text-orange-300' :
|
||||||
'text-zinc-500';
|
'text-zinc-500';
|
||||||
|
|
||||||
// ── Podium (shared) ──────────────────────────────────────────────────────────
|
// ── Podium tier styling (gold / silver / bronze) ───────────────────────────────
|
||||||
const TopPodium = ({ data, metricKey, numberFormat = {}, accentColor = 'border-yellow-400' }) => {
|
// Solid metal bars with dark, clearly-legible rank numerals. Height encodes rank:
|
||||||
if (data.length < 3) return null;
|
// 1st is tallest, then 2nd, then 3rd.
|
||||||
const [first, second, third] = data;
|
const PODIUM_TIERS = {
|
||||||
// Bars grow from a shared baseline at a constant speed, so the shortest (bronze)
|
1: {
|
||||||
// finishes first, then silver, and the tallest (gold) lands last.
|
heightPx: 200,
|
||||||
const Step = ({ agent, rank, heightPx, color, glow }) => (
|
border: 'border-yellow-400',
|
||||||
|
avatarRing: 'border-yellow-400',
|
||||||
|
bar: 'bg-gradient-to-b from-yellow-300 to-yellow-500',
|
||||||
|
numeral: 'text-yellow-900/80',
|
||||||
|
text: 'text-yellow-600 dark:text-yellow-400',
|
||||||
|
glow: 'bg-yellow-500',
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
heightPx: 144,
|
||||||
|
border: 'border-zinc-300',
|
||||||
|
avatarRing: 'border-zinc-300',
|
||||||
|
bar: 'bg-gradient-to-b from-zinc-200 to-zinc-400',
|
||||||
|
numeral: 'text-zinc-700/80',
|
||||||
|
text: 'text-zinc-500 dark:text-zinc-300',
|
||||||
|
glow: 'bg-zinc-400',
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
heightPx: 100,
|
||||||
|
border: 'border-orange-400',
|
||||||
|
avatarRing: 'border-orange-400',
|
||||||
|
bar: 'bg-gradient-to-b from-orange-300 to-orange-500',
|
||||||
|
numeral: 'text-orange-900/80',
|
||||||
|
text: 'text-orange-600 dark:text-orange-400',
|
||||||
|
glow: 'bg-orange-500',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// ── Podium step (single column) ───────────────────────────────────────────────
|
||||||
|
// Bars grow from a shared baseline at a constant speed, so the shortest (bronze)
|
||||||
|
// finishes first, then silver, and the tallest (gold) lands last.
|
||||||
|
const PodiumStep = ({ agent, rank, metricKey, numberFormat = {} }) => {
|
||||||
|
const tier = PODIUM_TIERS[rank];
|
||||||
|
return (
|
||||||
<div className="flex flex-col items-center z-10 mx-2 md:mx-4">
|
<div className="flex flex-col items-center z-10 mx-2 md:mx-4">
|
||||||
<div className="mb-4 text-center">
|
<div className="mb-4 text-center">
|
||||||
<div className="relative inline-block">
|
<div className="relative inline-block">
|
||||||
<div className={`w-16 h-16 md:w-20 md:h-20 rounded-full border-4 ${color} flex items-center justify-center bg-zinc-900/5 dark:bg-zinc-800 shadow-xl overflow-hidden mb-2 relative z-10`}>
|
<div className={`w-16 h-16 md:w-20 md:h-20 rounded-full border-4 ${tier.avatarRing} flex items-center justify-center bg-zinc-900/5 dark:bg-zinc-800 shadow-xl overflow-hidden mb-2 relative z-10`}>
|
||||||
<span className="text-xl font-bold text-zinc-700 dark:text-zinc-300">
|
<span className="text-xl font-bold text-zinc-700 dark:text-zinc-300">
|
||||||
{agent.name.split(' ').map(n => n[0]).join('')}
|
{agent.name.split(' ').map(n => n[0]).join('')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{rank === 1 && <Crown size={24} className="absolute -top-4 -right-2 text-yellow-400 fill-yellow-400 animate-bounce" />}
|
{rank === 1 && <Crown size={24} className="absolute -top-4 -right-2 text-yellow-400 fill-yellow-400 animate-bounce" />}
|
||||||
<div className={`absolute inset-0 rounded-full blur-xl opacity-40 ${glow}`} />
|
<div className={`absolute inset-0 rounded-full blur-xl opacity-40 ${tier.glow}`} />
|
||||||
</div>
|
</div>
|
||||||
<h3 className="font-bold text-sm md:text-base text-zinc-900 dark:text-white truncate max-w-[100px]">{agent.name}</h3>
|
<h3 className="font-bold text-sm md:text-base text-zinc-900 dark:text-white truncate max-w-[100px]">{agent.name}</h3>
|
||||||
<p className={`font-mono font-bold text-sm md:text-lg ${color.replace('border-', 'text-')}`}>
|
<p className={`font-mono font-bold text-sm md:text-lg ${tier.text}`}>
|
||||||
<AnimatedNumber value={agent[metricKey]} duration={1.4} {...numberFormat} />
|
<AnimatedNumber value={agent[metricKey]} duration={1.4} {...numberFormat} />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ height: 0 }}
|
initial={{ height: 0 }}
|
||||||
animate={{ height: heightPx }}
|
animate={{ height: tier.heightPx }}
|
||||||
transition={{ duration: heightPx / 100, ease: 'easeOut' }}
|
transition={{ duration: tier.heightPx / 100, ease: 'easeOut' }}
|
||||||
className={`w-24 md:w-32 rounded-t-lg bg-gradient-to-b from-white/80 to-white/20 dark:from-white/10 dark:to-white/5 border-t border-x ${color} backdrop-blur-md relative overflow-hidden hover:brightness-110`}
|
className={`w-24 md:w-32 rounded-t-lg ${tier.bar} border-t border-x ${tier.border} shadow-lg relative overflow-hidden hover:brightness-110`}
|
||||||
>
|
>
|
||||||
<div className="absolute inset-x-0 top-0 h-[1px] bg-white/50" />
|
<div className="absolute inset-x-0 top-0 h-[2px] bg-white/50" />
|
||||||
<div className="w-full h-full flex items-end justify-center pb-4 text-4xl font-black text-black/5 dark:text-white/5 select-none">{rank}</div>
|
<div className={`w-full h-full flex items-end justify-center pb-4 text-5xl font-black ${tier.numeral} select-none`}>{rank}</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ── Podium (shared) ──────────────────────────────────────────────────────────
|
||||||
|
const TopPodium = ({ data, metricKey, numberFormat = {} }) => {
|
||||||
|
if (data.length < 3) return null;
|
||||||
|
const [first, second, third] = data;
|
||||||
return (
|
return (
|
||||||
// Fixed min-height reserves the full podium space so the shared baseline
|
// Fixed min-height reserves the full podium space so the shared baseline
|
||||||
// never shifts while the bars animate — they grow upward from the floor.
|
// never shifts while the bars animate — they grow upward from the floor.
|
||||||
<div className="flex items-end justify-center mb-12 pt-8 min-h-[360px]">
|
<div className="flex items-end justify-center mb-12 pt-8 min-h-[380px]">
|
||||||
<Step agent={second} rank={2} heightPx={128} color="border-zinc-300" glow="bg-zinc-400" />
|
<PodiumStep agent={second} rank={2} metricKey={metricKey} numberFormat={numberFormat} />
|
||||||
<Step agent={first} rank={1} heightPx={176} color={accentColor} glow="bg-yellow-500" />
|
<PodiumStep agent={first} rank={1} metricKey={metricKey} numberFormat={numberFormat} />
|
||||||
<Step agent={third} rank={3} heightPx={96} color="border-orange-400" glow="bg-orange-500" />
|
<PodiumStep agent={third} rank={3} metricKey={metricKey} numberFormat={numberFormat} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -262,7 +300,6 @@ const LeaderboardPage = () => {
|
|||||||
volume: { suffix: ' Deals' },
|
volume: { suffix: ' Deals' },
|
||||||
winRate: { suffix: '%', decimals: 1 },
|
winRate: { suffix: '%', decimals: 1 },
|
||||||
}[metric]}
|
}[metric]}
|
||||||
accentColor="border-yellow-400"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -357,7 +394,6 @@ const LeaderboardPage = () => {
|
|||||||
total: {},
|
total: {},
|
||||||
convRate: { suffix: '%', decimals: 1 },
|
convRate: { suffix: '%', decimals: 1 },
|
||||||
}[cvMetric]}
|
}[cvMetric]}
|
||||||
accentColor="border-emerald-400"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user