merge main
This commit is contained in:
commit
602e302f9f
|
@ -30,6 +30,7 @@ interface ChartProps {
|
|||
titleValue?: number
|
||||
useMulticoloredBars?: boolean
|
||||
zeroLine?: boolean
|
||||
loading?: boolean
|
||||
}
|
||||
|
||||
const Chart: FunctionComponent<ChartProps> = ({
|
||||
|
@ -47,6 +48,7 @@ const Chart: FunctionComponent<ChartProps> = ({
|
|||
titleValue,
|
||||
useMulticoloredBars,
|
||||
zeroLine,
|
||||
loading,
|
||||
}) => {
|
||||
const [mouseData, setMouseData] = useState<string | null>(null)
|
||||
const [daysToShow, setDaysToShow] = useState(daysRange || 30)
|
||||
|
@ -83,268 +85,292 @@ const Chart: FunctionComponent<ChartProps> = ({
|
|||
|
||||
return (
|
||||
<div className="h-52 w-full" ref={observe}>
|
||||
<div className="flex w-full items-start justify-between pb-6">
|
||||
<div className="pl-2">
|
||||
<div className="pb-0.5 text-xs text-th-fgd-3">{title}</div>
|
||||
{mouseData ? (
|
||||
<>
|
||||
<div className="pb-1 text-xl font-bold text-th-fgd-1">
|
||||
{labelFormat(mouseData[yAxis])}
|
||||
{data.length > 0 ? (
|
||||
<>
|
||||
<div className="flex w-full items-start justify-between pb-6">
|
||||
<div className="pl-2">
|
||||
<div className="pb-0.5 text-xs text-th-fgd-3">{title}</div>
|
||||
{mouseData ? (
|
||||
<>
|
||||
<div className="pb-1 text-xl font-bold text-th-fgd-1">
|
||||
{labelFormat(mouseData[yAxis])}
|
||||
</div>
|
||||
<div className="text-xs font-normal text-th-fgd-4">
|
||||
{dayjs(mouseData[xAxis]).format('ddd MMM D YYYY, h:mma')}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="pb-1 text-xl font-bold text-th-fgd-1">
|
||||
{titleValue
|
||||
? labelFormat(titleValue)
|
||||
: labelFormat(data[data.length - 1][yAxis])}
|
||||
</div>
|
||||
<div className="h-4 text-xs font-normal text-th-fgd-4">
|
||||
{titleValue
|
||||
? ''
|
||||
: dayjs(data[data.length - 1][xAxis]).format(
|
||||
'ddd MMM D YYYY, h:mma'
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{!hideRangeFilters ? (
|
||||
<div className="flex h-5">
|
||||
<button
|
||||
className={`default-transition mx-3 text-xs font-bold text-th-fgd-1 focus:outline-none md:hover:text-th-primary ${
|
||||
daysToShow === 1 && 'text-th-primary'
|
||||
}`}
|
||||
onClick={() => setDaysToShow(1)}
|
||||
>
|
||||
24H
|
||||
</button>
|
||||
<button
|
||||
className={`default-transition mx-3 text-xs font-bold text-th-fgd-1 focus:outline-none md:hover:text-th-primary ${
|
||||
daysToShow === 7 && 'text-th-primary'
|
||||
}`}
|
||||
onClick={() => setDaysToShow(7)}
|
||||
>
|
||||
7D
|
||||
</button>
|
||||
<button
|
||||
className={`default-transition ml-3 text-xs font-bold text-th-fgd-1 focus:outline-none md:hover:text-th-primary ${
|
||||
daysToShow === 30 && 'text-th-primary'
|
||||
}`}
|
||||
onClick={() => setDaysToShow(30)}
|
||||
>
|
||||
30D
|
||||
</button>
|
||||
{showAll ? (
|
||||
<button
|
||||
className={`default-transition ml-3 text-xs font-bold text-th-fgd-1 focus:outline-none md:hover:text-th-primary ${
|
||||
daysToShow === 1000 && 'text-th-primary'
|
||||
}`}
|
||||
onClick={() => setDaysToShow(1000)}
|
||||
>
|
||||
All
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="text-xs font-normal text-th-fgd-4">
|
||||
{dayjs(mouseData[xAxis]).format('ddd MMM D YYYY, h:mma')}
|
||||
</div>
|
||||
</>
|
||||
) : data.length > 0 ? (
|
||||
<>
|
||||
<div className="pb-1 text-xl font-bold text-th-fgd-1">
|
||||
{titleValue
|
||||
? labelFormat(titleValue)
|
||||
: labelFormat(data[data.length - 1][yAxis])}
|
||||
</div>
|
||||
<div className="h-4 text-xs font-normal text-th-fgd-4">
|
||||
{titleValue
|
||||
? ''
|
||||
: dayjs(data[data.length - 1][xAxis]).format(
|
||||
'ddd MMM D YYYY, h:mma'
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="mt-1 h-8 w-48 animate-pulse rounded bg-th-bkg-3" />
|
||||
<div className="mt-1 h-4 w-24 animate-pulse rounded bg-th-bkg-3" />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{!hideRangeFilters ? (
|
||||
<div className="flex h-5">
|
||||
<button
|
||||
className={`default-transition mx-3 text-xs font-bold text-th-fgd-1 focus:outline-none md:hover:text-th-primary ${
|
||||
daysToShow === 1 && 'text-th-primary'
|
||||
}`}
|
||||
onClick={() => setDaysToShow(1)}
|
||||
>
|
||||
24H
|
||||
</button>
|
||||
<button
|
||||
className={`default-transition mx-3 text-xs font-bold text-th-fgd-1 focus:outline-none md:hover:text-th-primary ${
|
||||
daysToShow === 7 && 'text-th-primary'
|
||||
}`}
|
||||
onClick={() => setDaysToShow(7)}
|
||||
>
|
||||
7D
|
||||
</button>
|
||||
<button
|
||||
className={`default-transition ml-3 text-xs font-bold text-th-fgd-1 focus:outline-none md:hover:text-th-primary ${
|
||||
daysToShow === 30 && 'text-th-primary'
|
||||
}`}
|
||||
onClick={() => setDaysToShow(30)}
|
||||
>
|
||||
30D
|
||||
</button>
|
||||
{showAll ? (
|
||||
<button
|
||||
className={`default-transition ml-3 text-xs font-bold text-th-fgd-1 focus:outline-none md:hover:text-th-primary ${
|
||||
daysToShow === 1000 && 'text-th-primary'
|
||||
}`}
|
||||
onClick={() => setDaysToShow(1000)}
|
||||
>
|
||||
All
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
{width > 0 && type === 'area' ? (
|
||||
<AreaChart
|
||||
width={width}
|
||||
height={height}
|
||||
data={data ? handleDaysToShow(daysToShow) : null}
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
<Tooltip
|
||||
cursor={{
|
||||
strokeOpacity: 0,
|
||||
}}
|
||||
content={<></>}
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient id="gradientArea" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stopColor="#ffba24" stopOpacity={1} />
|
||||
<stop offset="100%" stopColor="#ffba24" stopOpacity={0} />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<Area
|
||||
isAnimationActive={false}
|
||||
type="monotone"
|
||||
dataKey={yAxis}
|
||||
stroke="#ffba24"
|
||||
fill="url(#gradientArea)"
|
||||
/>
|
||||
<XAxis
|
||||
dataKey={xAxis}
|
||||
axisLine={false}
|
||||
hide={data.length > 0 ? false : true}
|
||||
dy={10}
|
||||
minTickGap={20}
|
||||
tick={{
|
||||
fill:
|
||||
theme === 'Light'
|
||||
? 'rgba(0,0,0,0.4)'
|
||||
: 'rgba(255,255,255,0.35)',
|
||||
fontSize: 10,
|
||||
}}
|
||||
tickLine={false}
|
||||
tickFormatter={(v) => formatDateAxis(v)}
|
||||
/>
|
||||
<YAxis
|
||||
dataKey={yAxis}
|
||||
axisLine={false}
|
||||
hide={data.length > 0 ? false : true}
|
||||
dx={-10}
|
||||
domain={['dataMin', 'dataMax']}
|
||||
tick={{
|
||||
fill:
|
||||
theme === 'Light'
|
||||
? 'rgba(0,0,0,0.4)'
|
||||
: 'rgba(255,255,255,0.35)',
|
||||
fontSize: 10,
|
||||
}}
|
||||
tickLine={false}
|
||||
tickFormatter={
|
||||
tickFormat
|
||||
? (v) => tickFormat(v)
|
||||
: (v) => numberCompactFormatter.format(v)
|
||||
}
|
||||
type="number"
|
||||
width={yAxisWidth || 50}
|
||||
/>
|
||||
{zeroLine ? (
|
||||
<ReferenceLine
|
||||
y={0}
|
||||
stroke={
|
||||
theme === 'Light' ? 'rgba(0,0,0,0.4)' : 'rgba(255,255,255,0.35)'
|
||||
}
|
||||
strokeDasharray="3 3"
|
||||
/>
|
||||
) : null}
|
||||
</AreaChart>
|
||||
) : null}
|
||||
{width > 0 && type === 'bar' ? (
|
||||
<BarChart
|
||||
width={width}
|
||||
height={height}
|
||||
data={
|
||||
data
|
||||
? hideRangeFilters
|
||||
? data
|
||||
: handleDaysToShow(daysToShow)
|
||||
: null
|
||||
}
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
<Tooltip
|
||||
cursor={{
|
||||
fill: '#fff',
|
||||
opacity: 0.2,
|
||||
}}
|
||||
content={<></>}
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient id="defaultGradientBar" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stopColor="#ffba24" stopOpacity={1} />
|
||||
<stop offset="100%" stopColor="#ffba24" stopOpacity={0.5} />
|
||||
</linearGradient>
|
||||
<linearGradient id="greenGradientBar" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop
|
||||
offset="0%"
|
||||
stopColor={theme === 'Mango' ? '#AFD803' : '#5EBF4D'}
|
||||
stopOpacity={1}
|
||||
{width > 0 && type === 'area' ? (
|
||||
<AreaChart
|
||||
width={width}
|
||||
height={height}
|
||||
data={data ? handleDaysToShow(daysToShow) : null}
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
<Tooltip
|
||||
cursor={{
|
||||
strokeOpacity: 0,
|
||||
}}
|
||||
content={<></>}
|
||||
/>
|
||||
<stop
|
||||
offset="100%"
|
||||
stopColor={theme === 'Mango' ? '#91B503' : '#4BA53B'}
|
||||
stopOpacity={1}
|
||||
<defs>
|
||||
<linearGradient id="gradientArea" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stopColor="#ffba24" stopOpacity={1} />
|
||||
<stop offset="100%" stopColor="#ffba24" stopOpacity={0} />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<Area
|
||||
isAnimationActive={false}
|
||||
type="monotone"
|
||||
dataKey={yAxis}
|
||||
stroke="#ffba24"
|
||||
fill="url(#gradientArea)"
|
||||
/>
|
||||
</linearGradient>
|
||||
<linearGradient id="redGradientBar" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop
|
||||
offset="0%"
|
||||
stopColor={theme === 'Mango' ? '#F84638' : '#CC2929'}
|
||||
stopOpacity={1}
|
||||
<XAxis
|
||||
dataKey={xAxis}
|
||||
axisLine={false}
|
||||
hide={data.length > 0 ? false : true}
|
||||
dy={10}
|
||||
minTickGap={20}
|
||||
tick={{
|
||||
fill:
|
||||
theme === 'Light'
|
||||
? 'rgba(0,0,0,0.4)'
|
||||
: 'rgba(255,255,255,0.35)',
|
||||
fontSize: 10,
|
||||
}}
|
||||
tickLine={false}
|
||||
tickFormatter={(v) => formatDateAxis(v)}
|
||||
/>
|
||||
<stop
|
||||
offset="100%"
|
||||
stopColor={theme === 'Mango' ? '#EC1809' : '#BB2525'}
|
||||
stopOpacity={1}
|
||||
/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<Bar dataKey={yAxis}>
|
||||
{data.map((entry, index) => (
|
||||
<Cell
|
||||
key={`cell-${index}`}
|
||||
fill={
|
||||
useMulticoloredBars
|
||||
? entry[yAxis] > 0
|
||||
? 'url(#greenGradientBar)'
|
||||
: 'url(#redGradientBar)'
|
||||
: 'url(#defaultGradientBar)'
|
||||
<YAxis
|
||||
dataKey={yAxis}
|
||||
axisLine={false}
|
||||
hide={data.length > 0 ? false : true}
|
||||
dx={-10}
|
||||
domain={['dataMin', 'dataMax']}
|
||||
tick={{
|
||||
fill:
|
||||
theme === 'Light'
|
||||
? 'rgba(0,0,0,0.4)'
|
||||
: 'rgba(255,255,255,0.35)',
|
||||
fontSize: 10,
|
||||
}}
|
||||
tickLine={false}
|
||||
tickFormatter={
|
||||
tickFormat
|
||||
? (v) => tickFormat(v)
|
||||
: (v) => numberCompactFormatter.format(v)
|
||||
}
|
||||
type="number"
|
||||
width={yAxisWidth || 50}
|
||||
/>
|
||||
))}
|
||||
</Bar>
|
||||
<XAxis
|
||||
dataKey={xAxis}
|
||||
axisLine={false}
|
||||
hide={data.length > 0 ? false : true}
|
||||
dy={10}
|
||||
minTickGap={20}
|
||||
tick={{
|
||||
fill:
|
||||
theme === 'Light'
|
||||
? 'rgba(0,0,0,0.4)'
|
||||
: 'rgba(255,255,255,0.35)',
|
||||
fontSize: 10,
|
||||
}}
|
||||
tickLine={false}
|
||||
tickFormatter={(v) => formatDateAxis(v)}
|
||||
/>
|
||||
<YAxis
|
||||
dataKey={yAxis}
|
||||
interval="preserveStartEnd"
|
||||
axisLine={false}
|
||||
hide={data.length > 0 ? false : true}
|
||||
dx={-10}
|
||||
tick={{
|
||||
fill:
|
||||
theme === 'Light'
|
||||
? 'rgba(0,0,0,0.4)'
|
||||
: 'rgba(255,255,255,0.35)',
|
||||
fontSize: 10,
|
||||
}}
|
||||
tickLine={false}
|
||||
tickFormatter={
|
||||
tickFormat
|
||||
? (v) => tickFormat(v)
|
||||
: (v) => numberCompactFormatter.format(v)
|
||||
}
|
||||
type="number"
|
||||
width={yAxisWidth || 50}
|
||||
/>
|
||||
{zeroLine ? (
|
||||
<ReferenceLine
|
||||
y={0}
|
||||
stroke={
|
||||
theme === 'Light' ? 'rgba(0,0,0,0.4)' : 'rgba(255,255,255,0.2)'
|
||||
}
|
||||
/>
|
||||
{zeroLine ? (
|
||||
<ReferenceLine
|
||||
y={0}
|
||||
stroke={
|
||||
theme === 'Light'
|
||||
? 'rgba(0,0,0,0.4)'
|
||||
: 'rgba(255,255,255,0.35)'
|
||||
}
|
||||
strokeDasharray="3 3"
|
||||
/>
|
||||
) : null}
|
||||
</AreaChart>
|
||||
) : null}
|
||||
</BarChart>
|
||||
) : null}
|
||||
{width > 0 && type === 'bar' ? (
|
||||
<BarChart
|
||||
width={width}
|
||||
height={height}
|
||||
data={
|
||||
data
|
||||
? hideRangeFilters
|
||||
? data
|
||||
: handleDaysToShow(daysToShow)
|
||||
: null
|
||||
}
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
<Tooltip
|
||||
cursor={{
|
||||
fill: '#fff',
|
||||
opacity: 0.2,
|
||||
}}
|
||||
content={<></>}
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="defaultGradientBar"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="0"
|
||||
y2="1"
|
||||
>
|
||||
<stop offset="0%" stopColor="#ffba24" stopOpacity={1} />
|
||||
<stop offset="100%" stopColor="#ffba24" stopOpacity={0.5} />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="greenGradientBar"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="0"
|
||||
y2="1"
|
||||
>
|
||||
<stop
|
||||
offset="0%"
|
||||
stopColor={theme === 'Mango' ? '#AFD803' : '#5EBF4D'}
|
||||
stopOpacity={1}
|
||||
/>
|
||||
<stop
|
||||
offset="100%"
|
||||
stopColor={theme === 'Mango' ? '#91B503' : '#4BA53B'}
|
||||
stopOpacity={1}
|
||||
/>
|
||||
</linearGradient>
|
||||
<linearGradient id="redGradientBar" x1="0" y1="1" x2="0" y2="0">
|
||||
<stop
|
||||
offset="0%"
|
||||
stopColor={theme === 'Mango' ? '#F84638' : '#CC2929'}
|
||||
stopOpacity={1}
|
||||
/>
|
||||
<stop
|
||||
offset="100%"
|
||||
stopColor={theme === 'Mango' ? '#EC1809' : '#BB2525'}
|
||||
stopOpacity={1}
|
||||
/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<Bar dataKey={yAxis}>
|
||||
{data.map((entry, index) => (
|
||||
<Cell
|
||||
key={`cell-${index}`}
|
||||
fill={
|
||||
useMulticoloredBars
|
||||
? entry[yAxis] > 0
|
||||
? 'url(#greenGradientBar)'
|
||||
: 'url(#redGradientBar)'
|
||||
: 'url(#defaultGradientBar)'
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</Bar>
|
||||
<XAxis
|
||||
dataKey={xAxis}
|
||||
axisLine={false}
|
||||
hide={data.length > 0 ? false : true}
|
||||
dy={10}
|
||||
minTickGap={20}
|
||||
tick={{
|
||||
fill:
|
||||
theme === 'Light'
|
||||
? 'rgba(0,0,0,0.4)'
|
||||
: 'rgba(255,255,255,0.35)',
|
||||
fontSize: 10,
|
||||
}}
|
||||
tickLine={false}
|
||||
tickFormatter={(v) => formatDateAxis(v)}
|
||||
/>
|
||||
<YAxis
|
||||
dataKey={yAxis}
|
||||
interval="preserveStartEnd"
|
||||
axisLine={false}
|
||||
hide={data.length > 0 ? false : true}
|
||||
dx={-10}
|
||||
tick={{
|
||||
fill:
|
||||
theme === 'Light'
|
||||
? 'rgba(0,0,0,0.4)'
|
||||
: 'rgba(255,255,255,0.35)',
|
||||
fontSize: 10,
|
||||
}}
|
||||
tickLine={false}
|
||||
tickFormatter={
|
||||
tickFormat
|
||||
? (v) => tickFormat(v)
|
||||
: (v) => numberCompactFormatter.format(v)
|
||||
}
|
||||
type="number"
|
||||
width={yAxisWidth || 50}
|
||||
/>
|
||||
{zeroLine ? (
|
||||
<ReferenceLine
|
||||
y={0}
|
||||
stroke={
|
||||
theme === 'Light'
|
||||
? 'rgba(0,0,0,0.4)'
|
||||
: 'rgba(255,255,255,0.2)'
|
||||
}
|
||||
/>
|
||||
) : null}
|
||||
</BarChart>
|
||||
) : null}
|
||||
</>
|
||||
) : loading ? (
|
||||
<>
|
||||
<div className="mt-1 h-8 w-48 animate-pulse rounded bg-th-bkg-3" />
|
||||
<div className="mt-1 h-4 w-24 animate-pulse rounded bg-th-bkg-3" />
|
||||
</>
|
||||
) : (
|
||||
<div className="flex h-full w-full items-center justify-center">
|
||||
<p className="mb-0">Chart not available</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -89,8 +89,7 @@ const TradeHistoryFilterModal: FunctionComponent<
|
|||
return {
|
||||
...prevSelected,
|
||||
size: {
|
||||
condition: (size) =>
|
||||
parseFloat(size) >= from && parseFloat(size) <= to,
|
||||
condition: (size) => size >= from && size <= to,
|
||||
values: { from: from, to: to },
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,13 +1,22 @@
|
|||
import { useState } from 'react'
|
||||
import { useMemo, useState } from 'react'
|
||||
import Chart from '../Chart'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import TabButtons from 'components/TabButtons'
|
||||
|
||||
export default function StatsAssets({ latestStats, stats }) {
|
||||
export default function StatsAssets({
|
||||
latestStats,
|
||||
stats,
|
||||
loadHistoricalStats,
|
||||
}) {
|
||||
const { t } = useTranslation('common')
|
||||
const [selectedAsset, setSelectedAsset] = useState<string>('BTC')
|
||||
|
||||
const selectedStatsData = stats.filter((stat) => stat.name === selectedAsset)
|
||||
const selectedStatsData = useMemo(() => {
|
||||
if (stats.length) {
|
||||
return stats.filter((stat) => stat.name === selectedAsset)
|
||||
}
|
||||
return []
|
||||
}, [stats, selectedAsset])
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -46,6 +55,7 @@ export default function StatsAssets({ latestStats, stats }) {
|
|||
x.toLocaleString(undefined, { maximumFractionDigits: 2 })
|
||||
}
|
||||
type="area"
|
||||
loading={loadHistoricalStats}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
|
@ -62,6 +72,7 @@ export default function StatsAssets({ latestStats, stats }) {
|
|||
(x * 100).toLocaleString(undefined, { maximumFractionDigits: 4 })
|
||||
}
|
||||
type="bar"
|
||||
loading={loadHistoricalStats}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
|
@ -77,6 +88,7 @@ export default function StatsAssets({ latestStats, stats }) {
|
|||
x.toLocaleString(undefined, { maximumFractionDigits: 2 })
|
||||
}
|
||||
type="area"
|
||||
loading={loadHistoricalStats}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
|
@ -93,6 +105,7 @@ export default function StatsAssets({ latestStats, stats }) {
|
|||
(x * 100).toLocaleString(undefined, { maximumFractionDigits: 4 })
|
||||
}
|
||||
type="bar"
|
||||
loading={loadHistoricalStats}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -36,7 +36,7 @@ function calculateFundingRate(
|
|||
return (fundingInQuoteDecimals / basePriceInBaseLots) * 100
|
||||
}
|
||||
|
||||
export default function StatsPerps({ perpStats }) {
|
||||
export default function StatsPerps({ perpStats, loadPerpStats }) {
|
||||
const { t } = useTranslation('common')
|
||||
const [selectedAsset, setSelectedAsset] = useState<string>('BTC-PERP')
|
||||
const marketConfigs = useMangoStore(
|
||||
|
@ -61,7 +61,7 @@ export default function StatsPerps({ perpStats }) {
|
|||
}, [selectedMarketConfig, perpMarkets])
|
||||
|
||||
const perpsData = useMemo(() => {
|
||||
if (perpStats.length === 0 || !selectedMarket) return []
|
||||
if (!perpStats.length || !selectedMarket) return []
|
||||
|
||||
let selectedStatsData = perpStats.filter(
|
||||
(stat) => stat.name === selectedAsset
|
||||
|
@ -160,6 +160,7 @@ export default function StatsPerps({ perpStats }) {
|
|||
}
|
||||
type="area"
|
||||
yAxisWidth={70}
|
||||
loading={loadPerpStats}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
|
@ -182,6 +183,7 @@ export default function StatsPerps({ perpStats }) {
|
|||
selectedMarketConfig.baseSymbol
|
||||
}
|
||||
type="area"
|
||||
loading={loadPerpStats}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
|
|
|
@ -32,7 +32,7 @@ const getAverageStats = (
|
|||
symbol: string,
|
||||
type: string
|
||||
): string => {
|
||||
if (stats?.length) {
|
||||
if (stats?.length > 0) {
|
||||
const priorDate = new Date(Date.now() - daysAgo * 24 * 60 * 60 * 1000)
|
||||
const selectedStatsData = stats.filter((s) => s.name === symbol)
|
||||
const timeFilteredStats = selectedStatsData.filter(
|
||||
|
@ -55,7 +55,11 @@ const getAverageStats = (
|
|||
return '-'
|
||||
}
|
||||
|
||||
export default function StatsTotals({ latestStats, stats }) {
|
||||
export default function StatsTotals({
|
||||
latestStats,
|
||||
stats,
|
||||
loadHistoricalStats,
|
||||
}) {
|
||||
const { t } = useTranslation('common')
|
||||
const { width } = useViewport()
|
||||
const isMobile = width ? width < breakpoints.sm : false
|
||||
|
@ -64,7 +68,7 @@ export default function StatsTotals({ latestStats, stats }) {
|
|||
const [depositValues, borrowValues]: [Values[], Values[]] = useMemo(() => {
|
||||
const depositValues: Values[] = []
|
||||
const borrowValues: Values[] = []
|
||||
for (let i = 0; i < stats.length; i++) {
|
||||
for (let i = 0; i < stats?.length; i++) {
|
||||
const time = stats[i].hourly
|
||||
const name = stats[i].name
|
||||
const depositValue =
|
||||
|
@ -149,6 +153,7 @@ export default function StatsTotals({ latestStats, stats }) {
|
|||
'$' + x.toLocaleString(undefined, { maximumFractionDigits: 0 })
|
||||
}
|
||||
type="area"
|
||||
loading={loadHistoricalStats}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
|
@ -165,6 +170,7 @@ export default function StatsTotals({ latestStats, stats }) {
|
|||
'$' + x.toLocaleString(undefined, { maximumFractionDigits: 0 })
|
||||
}
|
||||
type="area"
|
||||
loading={loadHistoricalStats}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -250,9 +256,9 @@ export default function StatsTotals({ latestStats, stats }) {
|
|||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="pb-8">
|
||||
<h2 className="mb-4">{t('average-deposit')}</h2>
|
||||
{stats.length > 1 ? (
|
||||
{stats?.length > 0 ? (
|
||||
<div className="pb-8">
|
||||
<h2 className="mb-4">{t('average-deposit')}</h2>
|
||||
<Table>
|
||||
<thead>
|
||||
<TrHead>
|
||||
|
@ -290,60 +296,62 @@ export default function StatsTotals({ latestStats, stats }) {
|
|||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
) : (
|
||||
<>
|
||||
<div className="h-8 w-full animate-pulse rounded bg-th-bkg-3" />
|
||||
<div className="mt-1 h-8 w-full animate-pulse rounded bg-th-bkg-3" />
|
||||
<div className="mt-1 h-8 w-full animate-pulse rounded bg-th-bkg-3" />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<h2 className="mb-4">{t('average-borrow')}</h2>
|
||||
{stats.length > 1 ? (
|
||||
<Table>
|
||||
<thead>
|
||||
<TrHead>
|
||||
<Th>{t('asset')}</Th>
|
||||
<Th>24h</Th>
|
||||
<Th>7d</Th>
|
||||
<Th>30d</Th>
|
||||
</TrHead>
|
||||
</thead>
|
||||
<tbody>
|
||||
{latestStats.map((stat) => (
|
||||
<TrBody key={stat.name}>
|
||||
<Td>
|
||||
<div className="flex items-center">
|
||||
<img
|
||||
alt=""
|
||||
width="20"
|
||||
height="20"
|
||||
src={`/assets/icons/${stat.name.toLowerCase()}.svg`}
|
||||
className={`mr-2.5`}
|
||||
/>
|
||||
{stat.name}
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
{getAverageStats(stats, 1, stat.name, 'borrowIndex')}
|
||||
</Td>
|
||||
<Td>
|
||||
{getAverageStats(stats, 7, stat.name, 'borrowIndex')}
|
||||
</Td>
|
||||
<Td>
|
||||
{getAverageStats(stats, 30, stat.name, 'borrowIndex')}
|
||||
</Td>
|
||||
</TrBody>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
) : (
|
||||
</div>
|
||||
) : loadHistoricalStats ? (
|
||||
<>
|
||||
<div className="h-8 w-full animate-pulse rounded bg-th-bkg-3" />
|
||||
<div className="mt-1 h-8 w-full animate-pulse rounded bg-th-bkg-3" />
|
||||
<div className="mt-1 h-8 w-full animate-pulse rounded bg-th-bkg-3" />
|
||||
</>
|
||||
)}
|
||||
) : null}
|
||||
{stats?.length > 0 ? (
|
||||
<>
|
||||
<h2 className="mb-4">{t('average-borrow')}</h2>
|
||||
<Table>
|
||||
<thead>
|
||||
<TrHead>
|
||||
<Th>{t('asset')}</Th>
|
||||
<Th>24h</Th>
|
||||
<Th>7d</Th>
|
||||
<Th>30d</Th>
|
||||
</TrHead>
|
||||
</thead>
|
||||
<tbody>
|
||||
{latestStats.map((stat) => (
|
||||
<TrBody key={stat.name}>
|
||||
<Td>
|
||||
<div className="flex items-center">
|
||||
<img
|
||||
alt=""
|
||||
width="20"
|
||||
height="20"
|
||||
src={`/assets/icons/${stat.name.toLowerCase()}.svg`}
|
||||
className={`mr-2.5`}
|
||||
/>
|
||||
{stat.name}
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
{getAverageStats(stats, 1, stat.name, 'borrowIndex')}
|
||||
</Td>
|
||||
<Td>
|
||||
{getAverageStats(stats, 7, stat.name, 'borrowIndex')}
|
||||
</Td>
|
||||
<Td>
|
||||
{getAverageStats(stats, 30, stat.name, 'borrowIndex')}
|
||||
</Td>
|
||||
</TrBody>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
</>
|
||||
) : loadHistoricalStats ? (
|
||||
<>
|
||||
<div className="h-8 w-full animate-pulse rounded bg-th-bkg-3" />
|
||||
<div className="mt-1 h-8 w-full animate-pulse rounded bg-th-bkg-3" />
|
||||
<div className="mt-1 h-8 w-full animate-pulse rounded bg-th-bkg-3" />
|
||||
</>
|
||||
) : null}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
|
@ -416,80 +424,92 @@ export default function StatsTotals({ latestStats, stats }) {
|
|||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className="mb-8 border-b border-th-bkg-3">
|
||||
<h2 className="mb-4">{t('average-deposit')}</h2>
|
||||
{stats.length > 1
|
||||
? latestStats.map((stat) => (
|
||||
<Row key={stat.name}>
|
||||
<div className="grid grid-cols-12 grid-rows-2 text-left sm:grid-rows-1 sm:text-right">
|
||||
<div className="text-fgd-1 col-span-12 sm:col-span-3">
|
||||
<div className="flex items-center">
|
||||
<img
|
||||
alt=""
|
||||
width="20"
|
||||
height="20"
|
||||
src={`/assets/icons/${stat.name
|
||||
.split(/-|\//)[0]
|
||||
.toLowerCase()}.svg`}
|
||||
className={`mr-2.5`}
|
||||
/>
|
||||
{stat.name}
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-4 sm:col-span-3">
|
||||
<div className="pb-0.5 text-xs text-th-fgd-3">24h</div>
|
||||
{getAverageStats(stats, 1, stat.name, 'depositIndex')}
|
||||
</div>
|
||||
<div className="col-span-4 sm:col-span-3">
|
||||
<div className="pb-0.5 text-xs text-th-fgd-3">7d</div>
|
||||
{getAverageStats(stats, 7, stat.name, 'depositIndex')}
|
||||
</div>
|
||||
<div className="col-span-4 sm:col-span-3">
|
||||
<div className="pb-0.5 text-xs text-th-fgd-3">30d</div>
|
||||
{getAverageStats(stats, 30, stat.name, 'depositIndex')}
|
||||
{stats?.length > 0 ? (
|
||||
<div className="mb-8 border-b border-th-bkg-4">
|
||||
<h2 className="mb-4">{t('average-deposit')}</h2>
|
||||
{latestStats.map((stat) => (
|
||||
<Row key={stat.name}>
|
||||
<div className="grid grid-cols-12 grid-rows-2 text-left sm:grid-rows-1 sm:text-right">
|
||||
<div className="text-fgd-1 col-span-12 sm:col-span-3">
|
||||
<div className="flex items-center">
|
||||
<img
|
||||
alt=""
|
||||
width="20"
|
||||
height="20"
|
||||
src={`/assets/icons/${stat.name
|
||||
.split(/-|\//)[0]
|
||||
.toLowerCase()}.svg`}
|
||||
className={`mr-2.5`}
|
||||
/>
|
||||
{stat.name}
|
||||
</div>
|
||||
</div>
|
||||
</Row>
|
||||
))
|
||||
: null}
|
||||
</div>
|
||||
<div className="mb-4 border-b border-th-bkg-3">
|
||||
<h2 className="mb-4">{t('average-borrow')}</h2>
|
||||
{stats.length > 1
|
||||
? latestStats.map((stat) => (
|
||||
<Row key={stat.name}>
|
||||
<div className="grid grid-cols-12 grid-rows-2 gap-2 text-left sm:grid-rows-1 sm:text-right">
|
||||
<div className="text-fgd-1 col-span-12 flex items-center sm:col-span-3">
|
||||
<div className="flex items-center">
|
||||
<img
|
||||
alt=""
|
||||
width="20"
|
||||
height="20"
|
||||
src={`/assets/icons/${stat.name
|
||||
.split(/-|\//)[0]
|
||||
.toLowerCase()}.svg`}
|
||||
className={`mr-2.5`}
|
||||
/>
|
||||
{stat.name}
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-4 sm:col-span-3">
|
||||
<div className="pb-0.5 text-xs text-th-fgd-3">24h</div>
|
||||
{getAverageStats(stats, 1, stat.name, 'borrowIndex')}
|
||||
</div>
|
||||
<div className="col-span-4 sm:col-span-3">
|
||||
<div className="pb-0.5 text-xs text-th-fgd-3">7d</div>
|
||||
{getAverageStats(stats, 7, stat.name, 'borrowIndex')}
|
||||
</div>
|
||||
<div className="col-span-4 sm:col-span-3">
|
||||
<div className="pb-0.5 text-xs text-th-fgd-3">30d</div>
|
||||
{getAverageStats(stats, 30, stat.name, 'borrowIndex')}
|
||||
<div className="col-span-4 sm:col-span-3">
|
||||
<div className="pb-0.5 text-xs text-th-fgd-3">24h</div>
|
||||
{getAverageStats(stats, 1, stat.name, 'depositIndex')}
|
||||
</div>
|
||||
<div className="col-span-4 sm:col-span-3">
|
||||
<div className="pb-0.5 text-xs text-th-fgd-3">7d</div>
|
||||
{getAverageStats(stats, 7, stat.name, 'depositIndex')}
|
||||
</div>
|
||||
<div className="col-span-4 sm:col-span-3">
|
||||
<div className="pb-0.5 text-xs text-th-fgd-3">30d</div>
|
||||
{getAverageStats(stats, 30, stat.name, 'depositIndex')}
|
||||
</div>
|
||||
</div>
|
||||
</Row>
|
||||
))}
|
||||
</div>
|
||||
) : loadHistoricalStats ? (
|
||||
<>
|
||||
<div className="h-8 w-full animate-pulse rounded bg-th-bkg-3" />
|
||||
<div className="mt-1 h-8 w-full animate-pulse rounded bg-th-bkg-3" />
|
||||
<div className="mt-1 h-8 w-full animate-pulse rounded bg-th-bkg-3" />
|
||||
</>
|
||||
) : null}
|
||||
{stats?.length > 0 ? (
|
||||
<div className="mb-4 border-b border-th-bkg-4">
|
||||
<h2 className="mb-4">{t('average-borrow')}</h2>
|
||||
{latestStats.map((stat) => (
|
||||
<Row key={stat.name}>
|
||||
<div className="grid grid-cols-12 grid-rows-2 gap-2 text-left sm:grid-rows-1 sm:text-right">
|
||||
<div className="text-fgd-1 col-span-12 flex items-center sm:col-span-3">
|
||||
<div className="flex items-center">
|
||||
<img
|
||||
alt=""
|
||||
width="20"
|
||||
height="20"
|
||||
src={`/assets/icons/${stat.name
|
||||
.split(/-|\//)[0]
|
||||
.toLowerCase()}.svg`}
|
||||
className={`mr-2.5`}
|
||||
/>
|
||||
{stat.name}
|
||||
</div>
|
||||
</div>
|
||||
</Row>
|
||||
))
|
||||
: null}
|
||||
</div>
|
||||
<div className="col-span-4 sm:col-span-3">
|
||||
<div className="pb-0.5 text-xs text-th-fgd-3">24h</div>
|
||||
{getAverageStats(stats, 1, stat.name, 'borrowIndex')}
|
||||
</div>
|
||||
<div className="col-span-4 sm:col-span-3">
|
||||
<div className="pb-0.5 text-xs text-th-fgd-3">7d</div>
|
||||
{getAverageStats(stats, 7, stat.name, 'borrowIndex')}
|
||||
</div>
|
||||
<div className="col-span-4 sm:col-span-3">
|
||||
<div className="pb-0.5 text-xs text-th-fgd-3">30d</div>
|
||||
{getAverageStats(stats, 30, stat.name, 'borrowIndex')}
|
||||
</div>
|
||||
</div>
|
||||
</Row>
|
||||
))}
|
||||
</div>
|
||||
) : loadHistoricalStats ? (
|
||||
<>
|
||||
<div className="h-8 w-full animate-pulse rounded bg-th-bkg-3" />
|
||||
<div className="mt-1 h-8 w-full animate-pulse rounded bg-th-bkg-3" />
|
||||
<div className="mt-1 h-8 w-full animate-pulse rounded bg-th-bkg-3" />
|
||||
</>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
|
|
@ -29,6 +29,8 @@ const useMangoStats = () => {
|
|||
},
|
||||
])
|
||||
const [latestStats, setLatestStats] = useState<any[]>([])
|
||||
const [loadHistoricalStats, setLoadHistoricalStats] = useState<boolean>(false)
|
||||
const [loadPerpStats, setLoadPerpStats] = useState<boolean>(false)
|
||||
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
|
||||
const mangoGroupName = useMangoStore((s) => s.selectedMangoGroup.name)
|
||||
const connection = useMangoStore((s) => s.connection.current)
|
||||
|
@ -36,22 +38,34 @@ const useMangoStats = () => {
|
|||
|
||||
useEffect(() => {
|
||||
const fetchHistoricalStats = async () => {
|
||||
const response = await fetch(
|
||||
`https://mango-transaction-log.herokuapp.com/v3/stats/spot_stats_hourly?mango-group=${mangoGroupName}`
|
||||
)
|
||||
const stats = await response.json()
|
||||
setStats(stats)
|
||||
try {
|
||||
setLoadHistoricalStats(true)
|
||||
const response = await fetch(
|
||||
`https://mango-transaction-log.herokuapp.com/v3/stats/spot_stats_hourly?mango-group=${mangoGroupName}`
|
||||
)
|
||||
const stats = await response.json()
|
||||
setStats(stats)
|
||||
setLoadHistoricalStats(false)
|
||||
} catch {
|
||||
setLoadHistoricalStats(false)
|
||||
}
|
||||
}
|
||||
fetchHistoricalStats()
|
||||
}, [mangoGroupName])
|
||||
|
||||
useEffect(() => {
|
||||
const fetchHistoricalPerpStats = async () => {
|
||||
const response = await fetch(
|
||||
`https://mango-transaction-log.herokuapp.com/v3/stats/perp_stats_hourly?mango-group=${mangoGroupName}`
|
||||
)
|
||||
const stats = await response.json()
|
||||
setPerpStats(stats)
|
||||
try {
|
||||
setLoadPerpStats(true)
|
||||
const response = await fetch(
|
||||
`https://mango-transaction-log.herokuapp.com/v3/stats/perp_stats_hourly?mango-group=${mangoGroupName}`
|
||||
)
|
||||
const stats = await response.json()
|
||||
setPerpStats(stats)
|
||||
setLoadPerpStats(false)
|
||||
} catch {
|
||||
setLoadPerpStats(false)
|
||||
}
|
||||
}
|
||||
fetchHistoricalPerpStats()
|
||||
}, [mangoGroupName])
|
||||
|
@ -101,7 +115,7 @@ const useMangoStats = () => {
|
|||
getLatestStats()
|
||||
}, [mangoGroup])
|
||||
|
||||
return { latestStats, stats, perpStats }
|
||||
return { latestStats, stats, perpStats, loadHistoricalStats, loadPerpStats }
|
||||
}
|
||||
|
||||
export default useMangoStats
|
||||
|
|
104
pages/stats.tsx
104
pages/stats.tsx
|
@ -24,14 +24,9 @@ export async function getStaticProps({ locale }) {
|
|||
|
||||
export default function StatsPage() {
|
||||
const { t } = useTranslation('common')
|
||||
const TABS = [
|
||||
'Totals',
|
||||
'Assets',
|
||||
'Perps',
|
||||
// 'Markets',
|
||||
// 'Liquidations',
|
||||
]
|
||||
const { latestStats, stats, perpStats } = useMangoStats()
|
||||
const TABS = ['Totals', 'Assets', 'Perps']
|
||||
const { latestStats, stats, perpStats, loadHistoricalStats, loadPerpStats } =
|
||||
useMangoStats()
|
||||
const [viewIndex, setViewIndex] = useState(0)
|
||||
const [activeTab, setActiveTab] = useState(TABS[0])
|
||||
const { width } = useViewport()
|
||||
|
@ -51,49 +46,82 @@ export default function StatsPage() {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="pb-6">
|
||||
<div className="flex flex-col pb-4 pt-6 sm:flex-row">
|
||||
<div className="pt-6">
|
||||
<div className="pb-4">
|
||||
<h1>{t('stats')}</h1>
|
||||
</div>
|
||||
<div>
|
||||
{!isMobile ? (
|
||||
<Tabs activeTab={activeTab} onChange={handleTabChange} tabs={TABS} />
|
||||
) : (
|
||||
<SwipeableTabs
|
||||
onChange={handleChangeViewIndex}
|
||||
items={TABS}
|
||||
tabIndex={viewIndex}
|
||||
width="w-full"
|
||||
/>
|
||||
)}
|
||||
{!isMobile ? (
|
||||
<TabContent
|
||||
activeTab={activeTab}
|
||||
{!isMobile ? (
|
||||
<Tabs activeTab={activeTab} onChange={handleTabChange} tabs={TABS} />
|
||||
) : (
|
||||
<SwipeableTabs
|
||||
onChange={handleChangeViewIndex}
|
||||
items={TABS}
|
||||
tabIndex={viewIndex}
|
||||
width="w-full"
|
||||
/>
|
||||
)}
|
||||
{!isMobile ? (
|
||||
<TabContent
|
||||
activeTab={activeTab}
|
||||
latestStats={latestStats}
|
||||
perpStats={perpStats}
|
||||
stats={stats}
|
||||
loadHistoricalStats={loadHistoricalStats}
|
||||
loadPerpStats={loadPerpStats}
|
||||
/>
|
||||
) : (
|
||||
<Swipeable index={viewIndex} onChangeIndex={handleChangeViewIndex}>
|
||||
<StatsTotals
|
||||
latestStats={latestStats}
|
||||
perpStats={perpStats}
|
||||
stats={stats}
|
||||
loadHistoricalStats={loadHistoricalStats}
|
||||
/>
|
||||
) : (
|
||||
<Swipeable index={viewIndex} onChangeIndex={handleChangeViewIndex}>
|
||||
<StatsTotals latestStats={latestStats} stats={stats} />
|
||||
<StatsAssets latestStats={latestStats} stats={stats} />
|
||||
<StatsPerps perpStats={perpStats} />
|
||||
</Swipeable>
|
||||
)}
|
||||
</div>
|
||||
<StatsAssets
|
||||
latestStats={latestStats}
|
||||
stats={stats}
|
||||
loadHistoricalStats={loadHistoricalStats}
|
||||
/>
|
||||
<StatsPerps perpStats={perpStats} loadPerpStats={loadPerpStats} />
|
||||
</Swipeable>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const TabContent = ({ activeTab, latestStats, perpStats, stats }) => {
|
||||
const TabContent = ({
|
||||
activeTab,
|
||||
latestStats,
|
||||
perpStats,
|
||||
stats,
|
||||
loadHistoricalStats,
|
||||
loadPerpStats,
|
||||
}) => {
|
||||
switch (activeTab) {
|
||||
case 'Totals':
|
||||
return <StatsTotals latestStats={latestStats} stats={stats} />
|
||||
return (
|
||||
<StatsTotals
|
||||
latestStats={latestStats}
|
||||
stats={stats}
|
||||
loadHistoricalStats={loadHistoricalStats}
|
||||
/>
|
||||
)
|
||||
case 'Assets':
|
||||
return <StatsAssets latestStats={latestStats} stats={stats} />
|
||||
return (
|
||||
<StatsAssets
|
||||
latestStats={latestStats}
|
||||
stats={stats}
|
||||
loadHistoricalStats={loadHistoricalStats}
|
||||
/>
|
||||
)
|
||||
case 'Perps':
|
||||
return <StatsPerps perpStats={perpStats} />
|
||||
return <StatsPerps perpStats={perpStats} loadPerpStats={loadPerpStats} />
|
||||
default:
|
||||
return <StatsTotals latestStats={latestStats} stats={stats} />
|
||||
return (
|
||||
<StatsTotals
|
||||
latestStats={latestStats}
|
||||
stats={stats}
|
||||
loadHistoricalStats={loadHistoricalStats}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue