mango-ui-v2/components/LeaderboardTable.tsx

243 lines
9.3 KiB
TypeScript

import { useEffect, useState } from 'react'
import styled from '@emotion/styled'
import dayjs from 'dayjs'
import { Table, Thead, Tbody, Tr, Th, Td } from 'react-super-responsive-table'
import { AreaChart, Area, ReferenceLine, XAxis, YAxis, Tooltip } from 'recharts'
import { ExternalLinkIcon } from '@heroicons/react/outline'
import { usdFormatter } from '../utils'
import { AwardIcon, TrophyIcon } from './icons'
import useMangoStore from '../stores/useMangoStore'
const utc = require('dayjs/plugin/utc')
dayjs.extend(utc)
const StyledTooltipWrapper = styled.div`
min-width: 180px;
`
const LeaderboardTable = () => {
const [pnlHistory, setPnlHistory] = useState([])
const [loading, setLoading] = useState(false)
const pnlLeaderboard = useMangoStore((s) => s.pnlLeaderboard)
/* API Returns:
* [ { cumulative_pnl: -3.687498
date: "2021-06-10"
margin_account: "J8XtwLVyZjeH1PG1Nnk9cWbLn3zEemS1rCbn4x6AjtXM"
name: ""
owner: "APLKzSqJQw79q4U4ipBWnLdqkVzijSPNpDCNKwL8mW3B"
}, ... ]
*/
useEffect(() => {
const getPnlHistory = async () => {
setLoading(true)
const start = dayjs().utc().subtract(31, 'day').format('YYYY-MM-DD')
console.log(start)
const results = await Promise.all(
pnlLeaderboard.slice(pnlHistory.length).map(async (acc) => {
const response = await fetch(
`https://mango-transaction-log.herokuapp.com/stats/pnl_history/${acc.margin_account}?start_date=${start}`
)
const parsedResponse = await response.json()
return parsedResponse ? parsedResponse.reverse() : []
})
)
setPnlHistory(pnlHistory.concat(results))
setLoading(false)
}
getPnlHistory()
}, [pnlLeaderboard])
const formatPnlHistoryData = (data) => {
const start = new Date(
dayjs().utc().hour(0).minute(0).subtract(31, 'day')
).getTime()
return data.filter((d) => new Date(d.date).getTime() > start)
}
const tooltipContent = (tooltipProps) => {
if (tooltipProps.payload.length > 0) {
return (
<StyledTooltipWrapper className="bg-th-bkg-1 flex p-2 rounded">
<div>
<div className="text-th-fgd-3 text-xs">Date</div>
<div className="font-bold text-th-fgd-1 text-xs">
{tooltipProps.payload[0].payload.date}
</div>
</div>
<div className="pl-3">
<div className="text-th-fgd-3 text-xs">PNL</div>
<div className="font-bold text-th-fgd-1 text-xs">
{usdFormatter.format(
tooltipProps.payload[0].payload.cumulative_pnl
)}
</div>
</div>
</StyledTooltipWrapper>
)
}
return null
}
return (
<div className={`flex flex-col py-4`}>
<div className={`-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8`}>
<div className={`align-middle inline-block min-w-full sm:px-6 lg:px-8`}>
{pnlLeaderboard.length > 0 ? (
<div className={`shadow overflow-hidden border-b border-th-bkg-2`}>
<Table className={`min-w-full divide-y divide-th-bkg-2`}>
<Thead>
<Tr className="text-th-fgd-3 text-xs">
<Th
scope="col"
className={`px-6 py-3 text-left font-normal`}
>
Rank
</Th>
<Th
scope="col"
className={`px-6 py-3 text-left font-normal`}
>
Account
</Th>
<Th
scope="col"
className={`px-6 py-3 text-right font-normal`}
>
PNL
</Th>
<Th
scope="col"
className={`px-6 py-3 text-right font-normal`}
>
PNL / Time
</Th>
<Th
scope="col"
className={`px-6 py-3 text-right font-normal`}
>
<div className="flex items-center justify-start md:justify-end">
<span>View on Step</span>
<img
alt=""
width="20"
height="20"
src="/assets/icons/step.png"
className={`ml-1`}
/>
</div>
</Th>
</Tr>
</Thead>
<Tbody>
{pnlLeaderboard.map((acc, index) => (
<Tr
key={acc.margin_account}
className={`border-b border-th-bkg-3
${index % 2 === 0 ? `bg-th-bkg-3` : `bg-th-bkg-2`}
`}
>
<Td
className={`px-6 py-3 whitespace-nowrap text-sm text-th-fgd-1 w-8`}
>
<div className="flex items-center">
{acc.rank}
{acc.rank === 1 ? (
<TrophyIcon className="h-5 w-5 ml-1.5 text-th-primary" />
) : null}
{acc.rank === 2 || acc.rank === 3 ? (
<AwardIcon className="h-5 w-5 ml-1.5 text-th-primary-dark" />
) : null}
</div>
</Td>
<Td
className={`px-6 py-3 whitespace-nowrap text-left text-sm text-th-fgd-1 md:w-1/3`}
>
{acc.name
? acc.name
: `${acc.margin_account.slice(
0,
5
)}...${acc.margin_account.slice(-5)}`}
</Td>
<Td
className={`px-6 py-3 whitespace-nowrap text-sm text-th-fgd-1`}
>
<div className="flex md:justify-end">
{usdFormatter.format(acc.pnl)}
</div>
</Td>
<Td
className={`flex justify-end px-6 py-3 whitespace-nowrap`}
>
{loading && !pnlHistory[index] ? (
<div className="animate-pulse bg-th-fgd-4 h-14 opacity-10 rounded-md w-44" />
) : (
<AreaChart
width={176}
height={56}
data={
pnlHistory[index]
? formatPnlHistoryData(pnlHistory[index])
: null
}
>
<ReferenceLine
y={0}
stroke="#FF9C24"
strokeDasharray="3 3"
strokeOpacity={0.6}
/>
<Area
isAnimationActive={false}
type="monotone"
dataKey="cumulative_pnl"
stroke="#FF9C24"
fill="#FF9C24"
fillOpacity={0.1}
/>
<XAxis dataKey="date" hide />
<YAxis dataKey="cumulative_pnl" hide />
<Tooltip
content={tooltipContent}
position={{ x: 0, y: -50 }}
/>
</AreaChart>
)}
</Td>
<Td
className={`px-6 py-3 whitespace-nowrap text-sm text-th-fgd-1`}
>
<a
className="default-transition flex items-center md:justify-end text-th-fgd-2"
href={`https://app.step.finance/#/watch/${acc.owner}`}
target="_blank"
rel="noopener noreferrer"
>
<span>View</span>
<ExternalLinkIcon className={`h-4 w-4 ml-1.5`} />
</a>
</Td>
</Tr>
))}
</Tbody>
</Table>
</div>
) : (
<div className="pt-2">
<div className="animate-pulse bg-th-bkg-3 h-10 mb-2 rounded-md w-full" />
<div className="animate-pulse bg-th-bkg-3 h-10 mb-2 rounded-md w-full" />
<div className="animate-pulse bg-th-bkg-3 h-10 mb-2 rounded-md w-full" />
<div className="animate-pulse bg-th-bkg-3 h-10 mb-2 rounded-md w-full" />
<div className="animate-pulse bg-th-bkg-3 h-10 mb-2 rounded-md w-full" />
</div>
)}
</div>
</div>
</div>
)
}
export default LeaderboardTable