merge main

This commit is contained in:
saml33 2022-06-22 21:59:01 +10:00
commit 602e302f9f
7 changed files with 536 additions and 434 deletions

View File

@ -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>
)
}

View File

@ -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 },
},
}

View File

@ -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>

View File

@ -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>

View File

@ -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}
</>
)}
</>

View File

@ -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

View File

@ -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}
/>
)
}
}