Merge pull request #280 from blockworks-foundation/leaderboard-tweaks
leaderboard: more responsive/consistent layout
This commit is contained in:
commit
b7f095ebc6
|
@ -1,10 +1,13 @@
|
|||
import { useEffect, useMemo, useState } from 'react'
|
||||
import dayjs from 'dayjs'
|
||||
import { Table, Td, Th, TrBody, TrHead } from './TableElements'
|
||||
import { usdFormatter } from '../utils'
|
||||
import { AwardIcon, MedalIcon, ProfileIcon, TrophyIcon } from './icons'
|
||||
import { MedalIcon, ProfileIcon } from './icons'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { ChartPieIcon, TrendingUpIcon } from '@heroicons/react/outline'
|
||||
import {
|
||||
ChartPieIcon,
|
||||
ExternalLinkIcon,
|
||||
TrendingUpIcon,
|
||||
} from '@heroicons/react/outline'
|
||||
import { getProfilePicture } from '@solflare-wallet/pfp'
|
||||
import useMangoStore from '../stores/useMangoStore'
|
||||
import { connectionSelector } from '../stores/selectors'
|
||||
|
@ -13,7 +16,6 @@ const utc = require('dayjs/plugin/utc')
|
|||
dayjs.extend(utc)
|
||||
|
||||
const LeaderboardTable = ({ range = '29' }) => {
|
||||
const { t } = useTranslation('common')
|
||||
const [pnlLeaderboardData, setPnlLeaderboardData] = useState<any[]>([])
|
||||
const [perpPnlLeaderboardData, setPerpPnlLeaderboardData] = useState<any[]>(
|
||||
[]
|
||||
|
@ -87,164 +89,67 @@ const LeaderboardTable = ({ range = '29' }) => {
|
|||
|
||||
return (
|
||||
<div className="grid grid-cols-12 gap-6">
|
||||
<div className="col-span-12 flex space-x-3 lg:col-span-4 lg:flex-col lg:space-y-4 lg:space-x-0">
|
||||
<div className="col-span-12 flex space-x-3 lg:col-span-4 lg:flex-col lg:space-y-2 lg:space-x-0">
|
||||
<LeaderboardTypeButton
|
||||
leaderboardType={leaderboardType}
|
||||
setLeaderboardType={setLeaderboardType}
|
||||
range={range}
|
||||
label="total-pnl"
|
||||
icon={<ChartPieIcon className="mr-2 hidden h-6 w-6 lg:block" />}
|
||||
icon={<ChartPieIcon className="mr-3 hidden h-6 w-6 lg:block" />}
|
||||
/>
|
||||
<LeaderboardTypeButton
|
||||
leaderboardType={leaderboardType}
|
||||
setLeaderboardType={setLeaderboardType}
|
||||
range={range}
|
||||
label="futures-only"
|
||||
icon={<TrendingUpIcon className="mr-2 hidden h-6 w-6 lg:block" />}
|
||||
icon={<TrendingUpIcon className="mr-3 hidden h-6 w-6 lg:block" />}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-12 lg:col-span-8">
|
||||
{loading ? (
|
||||
<div className="mb-6 space-y-2">
|
||||
<div className="h-24 w-full animate-pulse rounded-lg bg-th-bkg-3" />
|
||||
<div className="h-24 w-full animate-pulse rounded-lg bg-th-bkg-3" />
|
||||
<div className="h-24 w-full animate-pulse rounded-lg bg-th-bkg-3" />
|
||||
{!loading ? (
|
||||
<div className="space-y-2">
|
||||
{leaderboardData.map((acc, i) => (
|
||||
<AccountCard
|
||||
rank={i + 1}
|
||||
acc={acc.mango_account}
|
||||
key={acc.mango_account}
|
||||
pnl={
|
||||
leaderboardType === 'total-pnl'
|
||||
? acc.pnl.toLocaleString('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'USD',
|
||||
maximumFractionDigits: 0,
|
||||
})
|
||||
: usdFormatter(acc.perp_pnl)
|
||||
}
|
||||
pfp={acc.pfp}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : leaderboardData.length > 0 ? (
|
||||
<div className="mb-6 space-y-2">
|
||||
<TopThreeCard
|
||||
rank={1}
|
||||
acc={leaderboardData[0].mango_account}
|
||||
pnl={
|
||||
leaderboardType === 'total-pnl'
|
||||
? usdFormatter(leaderboardData[0].pnl)
|
||||
: usdFormatter(leaderboardData[0].perp_pnl)
|
||||
}
|
||||
pfp={leaderboardData[0].pfp}
|
||||
/>
|
||||
<TopThreeCard
|
||||
rank={2}
|
||||
acc={leaderboardData[1].mango_account}
|
||||
pnl={
|
||||
leaderboardType === 'total-pnl'
|
||||
? usdFormatter(leaderboardData[1].pnl)
|
||||
: usdFormatter(leaderboardData[1].perp_pnl)
|
||||
}
|
||||
pfp={leaderboardData[0].pfp}
|
||||
/>
|
||||
<TopThreeCard
|
||||
rank={3}
|
||||
acc={leaderboardData[2].mango_account}
|
||||
pnl={
|
||||
leaderboardType === 'total-pnl'
|
||||
? usdFormatter(leaderboardData[2].pnl)
|
||||
: usdFormatter(leaderboardData[2].perp_pnl)
|
||||
}
|
||||
pfp={leaderboardData[0].pfp}
|
||||
/>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
<div className="h-20 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-20 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-20 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-20 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-20 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-20 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-20 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-20 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-20 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-20 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-20 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-20 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-20 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-20 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-20 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-20 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-20 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-20 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-20 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-20 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
</div>
|
||||
) : null}
|
||||
<div className={`overflow-x-auto sm:-mx-6 lg:-mx-8`}>
|
||||
<div
|
||||
className={`inline-block min-w-full align-middle sm:px-6 lg:px-8`}
|
||||
>
|
||||
{!loading ? (
|
||||
<div className={`overflow-hidden`}>
|
||||
<Table>
|
||||
<thead>
|
||||
<TrHead>
|
||||
<Th>{t('rank')}</Th>
|
||||
<Th>{t('account')}</Th>
|
||||
<Th>{t('pnl')}</Th>
|
||||
</TrHead>
|
||||
</thead>
|
||||
<tbody>
|
||||
{leaderboardData.slice(3).map((acc, index) => {
|
||||
const rank = index + 4
|
||||
return (
|
||||
<TrBody key={acc.mango_account}>
|
||||
<Td className="w-1/10">
|
||||
<div className="flex items-center">
|
||||
{rank}
|
||||
{rank === 1 ? (
|
||||
<TrophyIcon className="ml-1.5 h-5 w-5 text-th-primary" />
|
||||
) : null}
|
||||
{rank === 2 || rank === 3 ? (
|
||||
<AwardIcon className="ml-1.5 h-5 w-5 text-th-primary-dark" />
|
||||
) : null}
|
||||
</div>
|
||||
</Td>
|
||||
<Td className="w-1/3">
|
||||
<div className="flex items-center">
|
||||
<div className="mr-2 flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-th-bkg-4">
|
||||
{acc.pfp?.isAvailable ? (
|
||||
<img
|
||||
alt=""
|
||||
src={acc.pfp.url}
|
||||
className={`default-transition h-8 w-8 rounded-full hover:opacity-60
|
||||
`}
|
||||
/>
|
||||
) : (
|
||||
<ProfileIcon
|
||||
className={`h-5 w-5 text-th-fgd-3`}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{`${acc.mango_account.slice(
|
||||
0,
|
||||
5
|
||||
)}...${acc.mango_account.slice(-5)}`}
|
||||
</div>
|
||||
</Td>
|
||||
<Td className="w-1/3">
|
||||
{leaderboardType === 'total-pnl'
|
||||
? usdFormatter(acc.pnl)
|
||||
: usdFormatter(acc.perp_pnl)}
|
||||
</Td>
|
||||
<Td className="w-1/5">
|
||||
<div className="flex justify-end">
|
||||
<a
|
||||
href={`https://trade.mango.markets/account?pubkey=${acc.mango_account}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="default-transition text-th-fgd-3 hover:text-th-fgd-4"
|
||||
>
|
||||
{t('view-account')}
|
||||
</a>
|
||||
</div>
|
||||
</Td>
|
||||
</TrBody>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</Table>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-1">
|
||||
<div className="h-8 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-16 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-16 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-16 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-16 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-16 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-16 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-16 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-16 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-16 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-16 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-16 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-16 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-16 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-16 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-16 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-16 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-16 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-16 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
<div className="h-16 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
@ -252,8 +157,7 @@ const LeaderboardTable = ({ range = '29' }) => {
|
|||
|
||||
export default LeaderboardTable
|
||||
|
||||
const TopThreeCard = ({ rank, acc, pnl, pfp }) => {
|
||||
const { t } = useTranslation('common')
|
||||
const AccountCard = ({ rank, acc, pnl, pfp }) => {
|
||||
const medalColors =
|
||||
rank === 1
|
||||
? {
|
||||
|
@ -276,43 +180,47 @@ const TopThreeCard = ({ rank, acc, pnl, pfp }) => {
|
|||
lightest: '#EFBF8D',
|
||||
}
|
||||
return (
|
||||
<div className="flex items-center rounded-lg border border-th-bkg-4 p-4">
|
||||
<p className="mb-0 mr-4">{rank}</p>
|
||||
<div className="relative mr-3 flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-full bg-th-bkg-4">
|
||||
<MedalIcon
|
||||
className="absolute -top-2 -left-2 h-5 w-auto drop-shadow-lg"
|
||||
colors={medalColors}
|
||||
/>
|
||||
<a
|
||||
href={`https://trade.mango.markets/account?pubkey=${acc}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="default-transition flex items-center rounded-lg p-4 ring-1 ring-inset ring-th-bkg-4 hover:bg-th-bkg-3"
|
||||
>
|
||||
<p className="mb-0 mr-4 font-bold">{rank}</p>
|
||||
<div className="relative mr-3 flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-th-bkg-4">
|
||||
{rank < 4 ? (
|
||||
<MedalIcon
|
||||
className="absolute -top-2 -left-2 h-5 w-auto drop-shadow-lg"
|
||||
colors={medalColors}
|
||||
/>
|
||||
) : null}
|
||||
{pfp?.isAvailable ? (
|
||||
<img
|
||||
alt=""
|
||||
src={pfp.url}
|
||||
className={`default-transition h-10 w-10 rounded-full hover:opacity-60
|
||||
className={`default-transition h-12 w-12 rounded-full hover:opacity-60
|
||||
`}
|
||||
/>
|
||||
) : (
|
||||
<ProfileIcon className={`h-6 w-6 text-th-fgd-3`} />
|
||||
<ProfileIcon className={`h-7 w-7 text-th-fgd-3`} />
|
||||
)}
|
||||
</div>
|
||||
<div className="flex w-full flex-col sm:flex-row sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<p className="mb-0">{`${acc.slice(0, 5)}...${acc.slice(-5)}`}</p>
|
||||
<span className="text-base font-bold text-th-fgd-1 sm:text-lg">
|
||||
{pnl}
|
||||
</span>
|
||||
<p className="mb-0 text-th-fgd-2">{`${acc.slice(0, 5)}...${acc.slice(
|
||||
-5
|
||||
)}`}</p>
|
||||
</div>
|
||||
<div>
|
||||
<a
|
||||
href={`https://trade.mango.markets/account?pubkey=${acc}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="default-transition block text-th-fgd-3 hover:text-th-fgd-4"
|
||||
>
|
||||
{t('view-account')}
|
||||
</a>
|
||||
<div className="flex items-center">
|
||||
<span className={`text-base font-bold text-th-fgd-2 sm:text-lg`}>
|
||||
{pnl}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ExternalLinkIcon className="ml-3 h-4 w-4 flex-shrink-0 text-th-fgd-3" />
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -326,7 +234,7 @@ const LeaderboardTypeButton = ({
|
|||
const { t } = useTranslation('common')
|
||||
return (
|
||||
<button
|
||||
className={`relative flex w-full items-center justify-center rounded-md p-4 text-center lg:justify-start lg:text-left ${
|
||||
className={`relative flex w-full items-center justify-center rounded-md p-4 text-center lg:h-20 lg:justify-start lg:text-left ${
|
||||
leaderboardType === label
|
||||
? 'bg-th-bkg-4 text-th-fgd-1 after:absolute after:top-[100%] after:left-1/2 after:-translate-x-1/2 after:transform after:border-l-[12px] after:border-r-[12px] after:border-t-[12px] after:border-l-transparent after:border-t-th-bkg-4 after:border-r-transparent lg:after:left-[100%] lg:after:top-1/2 lg:after:-translate-x-0 lg:after:-translate-y-1/2 lg:after:border-r-0 lg:after:border-b-[12px] lg:after:border-t-transparent lg:after:border-b-transparent lg:after:border-l-th-bkg-4'
|
||||
: 'bg-th-bkg-3 text-th-fgd-4 hover:bg-th-bkg-4'
|
||||
|
@ -335,7 +243,7 @@ const LeaderboardTypeButton = ({
|
|||
>
|
||||
{icon}
|
||||
<div>
|
||||
<div className="font-bold sm:text-base">{t(label)}</div>
|
||||
<div className="font-bold sm:text-lg">{t(label)}</div>
|
||||
<span className="text-th-fgd-4">
|
||||
{range === '9999'
|
||||
? 'All-time'
|
||||
|
|
Loading…
Reference in New Issue