health pie chart

This commit is contained in:
saml33 2023-07-07 13:45:32 +10:00
parent d7896f1c30
commit 07472248f9
5 changed files with 298 additions and 89 deletions

View File

@ -459,100 +459,98 @@ const AccountPage = () => {
</button>
))}
</div>
<div className="md:h-24">
{activeTab === 'account-value' ? (
<div className="flex flex-col md:flex-row md:items-end md:space-x-6">
<div className="mx-auto mt-4 md:mx-0">
<div className="mb-2 flex justify-start font-display text-5xl text-th-fgd-1">
{animationSettings['number-scroll'] ? (
group && mangoAccount ? (
<FlipNumbers
height={48}
width={35}
play
delay={0.05}
duration={1}
numbers={formatCurrencyValue(accountValue, 2)}
/>
) : (
<FlipNumbers
height={48}
width={36}
play
delay={0.05}
duration={1}
numbers={'$0.00'}
/>
)
{activeTab === 'account-value' ? (
<div className="flex flex-col md:flex-row md:items-end md:space-x-6">
<div className="mx-auto mt-4 md:mx-0">
<div className="mb-2 flex justify-start font-display text-5xl text-th-fgd-1">
{animationSettings['number-scroll'] ? (
group && mangoAccount ? (
<FlipNumbers
height={48}
width={35}
play
delay={0.05}
duration={1}
numbers={formatCurrencyValue(accountValue, 2)}
/>
) : (
<FormatNumericValue
value={accountValue}
isUsd
decimals={2}
<FlipNumbers
height={48}
width={36}
play
delay={0.05}
duration={1}
numbers={'$0.00'}
/>
)}
</div>
<div className="flex items-center justify-center space-x-1.5 md:justify-start">
<Change change={accountValueChange} prefix="$" />
<p className="text-xs text-th-fgd-4">
{t('rolling-change')}
</p>
</div>
)
) : (
<FormatNumericValue
value={accountValue}
isUsd
decimals={2}
/>
)}
</div>
<div className="flex items-center justify-center space-x-1.5 md:justify-start">
<Change change={accountValueChange} prefix="$" />
<p className="text-xs text-th-fgd-4">{t('rolling-change')}</p>
</div>
{!performanceLoading ? (
oneDayPerformanceData.length ? (
<div
className="relative mt-4 flex h-40 items-end md:mt-0 md:h-20 md:w-52 lg:w-60"
onMouseEnter={() =>
onHoverMenu(showExpandChart, 'onMouseEnter')
}
onMouseLeave={() =>
onHoverMenu(showExpandChart, 'onMouseLeave')
}
>
<SimpleAreaChart
color={
accountValueChange >= 0
? COLORS.UP[theme]
: COLORS.DOWN[theme]
}
data={oneDayPerformanceData.concat(latestAccountData)}
name="accountValue"
xKey="time"
yKey="account_equity"
/>
<Transition
appear={true}
className="absolute right-2 bottom-2"
show={showExpandChart || isMobile}
enter="transition ease-in duration-300"
enterFrom="opacity-0 scale-75"
enterTo="opacity-100 scale-100"
leave="transition ease-out duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<IconButton
className="text-th-fgd-3"
hideBg
onClick={() => handleShowAccountValueChart()}
>
<ArrowsPointingOutIcon className="h-5 w-5" />
</IconButton>
</Transition>
</div>
) : null
) : mangoAccountAddress ? (
<SheenLoader className="mt-4 flex flex-1 md:mt-0">
<div className="h-40 w-full rounded-md bg-th-bkg-2 md:h-20 md:w-52 lg:w-60" />
</SheenLoader>
) : null}
</div>
) : null}
{activeTab === 'account:assets-liabilities' ? (
{!performanceLoading ? (
oneDayPerformanceData.length ? (
<div
className="relative mt-4 flex h-40 items-end md:mt-0 md:h-20 md:w-52 lg:w-60"
onMouseEnter={() =>
onHoverMenu(showExpandChart, 'onMouseEnter')
}
onMouseLeave={() =>
onHoverMenu(showExpandChart, 'onMouseLeave')
}
>
<SimpleAreaChart
color={
accountValueChange >= 0
? COLORS.UP[theme]
: COLORS.DOWN[theme]
}
data={oneDayPerformanceData.concat(latestAccountData)}
name="accountValue"
xKey="time"
yKey="account_equity"
/>
<Transition
appear={true}
className="absolute right-2 bottom-2"
show={showExpandChart || isMobile}
enter="transition ease-in duration-300"
enterFrom="opacity-0 scale-75"
enterTo="opacity-100 scale-100"
leave="transition ease-out duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<IconButton
className="text-th-fgd-3"
hideBg
onClick={() => handleShowAccountValueChart()}
>
<ArrowsPointingOutIcon className="h-5 w-5" />
</IconButton>
</Transition>
</div>
) : null
) : mangoAccountAddress ? (
<SheenLoader className="mt-4 flex flex-1 md:mt-0">
<div className="h-40 w-full rounded-md bg-th-bkg-2 md:h-20 md:w-52 lg:w-60" />
</SheenLoader>
) : null}
</div>
) : null}
{activeTab === 'account:assets-liabilities' ? (
<div className="w-full">
<AssetsLiabilities isMobile={isMobile} />
) : null}
</div>
</div>
) : null}
</div>
<div className="mt-6 mb-1 lg:mt-0">
<AccountActions />

View File

@ -0,0 +1,73 @@
// import { useTranslation } from 'next-i18next'
import HealthContributionsChart from './HealthContributionsChart'
import useMangoGroup from 'hooks/useMangoGroup'
import useMangoAccount from 'hooks/useMangoAccount'
import { useMemo } from 'react'
import { HealthType } from '@blockworks-foundation/mango-v4'
import { ArrowLeftIcon } from '@heroicons/react/20/solid'
export interface HealthContribution {
asset: string
contribution: number
isAsset: boolean
}
const HealthContributions = ({ hideView }: { hideView: () => void }) => {
// const { t } = useTranslation('account')
const { group } = useMangoGroup()
const { mangoAccount } = useMangoAccount()
const [initHealthContributions, maintHealthContributions] = useMemo(() => {
if (!group || !mangoAccount) return [[], []]
const init = mangoAccount
.getHealthContributionPerAssetUi(group, HealthType.init)
.filter((asset) => Math.abs(asset.contribution) > 0.01)
.map((item) => ({
...item,
contribution: Math.abs(item.contribution),
isAsset: item.contribution > 0 ? true : false,
}))
const maint = mangoAccount
.getHealthContributionPerAssetUi(group, HealthType.maint)
.filter((asset) => Math.abs(asset.contribution) > 0.01)
.map((item) => ({
...item,
contribution: Math.abs(item.contribution),
isAsset: item.contribution > 0 ? true : false,
}))
return [init, maint]
}, [group, mangoAccount])
console.log(initHealthContributions)
return (
<>
<div className="hide-scroll mb-3 flex h-14 items-center space-x-4 overflow-x-auto border-b border-th-bkg-3">
<button
className="flex h-14 w-14 flex-shrink-0 items-center justify-center border-r border-th-bkg-3 focus-visible:bg-th-bkg-3 md:hover:bg-th-bkg-2"
onClick={hideView}
>
<ArrowLeftIcon className="h-5 w-5" />
</button>
{/* <div className="flex space-x-2">
{CHART_TABS.map((tab) => (
<button
className={`whitespace-nowrap rounded-md py-1.5 px-2.5 text-sm font-medium focus-visible:bg-th-bkg-3 focus-visible:text-th-fgd-1 ${
chartToShow === tab
? 'bg-th-bkg-3 text-th-active md:hover:text-th-active'
: 'text-th-fgd-3 md:hover:text-th-fgd-2'
}`}
onClick={() => setChartToShow(tab)}
key={tab}
>
{t(tab)}
</button>
))}
</div> */}
</div>
<HealthContributionsChart data={maintHealthContributions} />
</>
)
}
export default HealthContributions

View File

@ -0,0 +1,126 @@
import { useTranslation } from 'next-i18next'
import { useTheme } from 'next-themes'
import { Cell, Pie, PieChart, Tooltip } from 'recharts'
import { COLORS } from 'styles/colors'
import { formatCurrencyValue } from 'utils/numbers'
import { formatTokenSymbol } from 'utils/tokens'
import { HealthContribution } from './HealthContributions'
interface CustomTooltipProps {
active?: boolean
contributions: HealthContribution[]
payload?: { payload: HealthContribution }[]
label?: string | number
}
const HealthContributionsChart = ({ data }: { data: HealthContribution[] }) => {
const { t } = useTranslation('account')
const { theme } = useTheme()
const pieSizes = { size: 160, outerRadius: 80, innerRadius: 64 }
const { size, outerRadius, innerRadius } = pieSizes
const CustomTooltip = ({
active,
contributions,
payload,
}: CustomTooltipProps) => {
if (active && payload) {
const isActivePayload = payload[0].payload.asset
const total = contributions.reduce((a, c) => {
const assetOrLiability = c.isAsset ? 1 : -1
return a + c.contribution * assetOrLiability
}, 0)
return (
<div className="rounded-md bg-th-bkg-2 p-4 focus:outline-none">
<div className="space-y-1">
{contributions
.sort((a, b) => b.contribution - a.contribution)
.map((asset) => {
const assetOrLiability = asset.isAsset ? 1 : -1
return (
<div
className="flex items-center justify-between"
key={asset.asset + asset.contribution}
>
<p
className={`whitespace-nowrap ${
isActivePayload === asset.asset ? 'text-th-active' : ''
}`}
>
{formatTokenSymbol(asset.asset)}
</p>
<p
className={`pl-4 font-mono ${
asset.isAsset ? 'text-th-up' : 'text-th-down'
}`}
>
{formatCurrencyValue(
asset.contribution * assetOrLiability
)}
</p>
</div>
)
})}
</div>
<div className="mt-3 flex justify-between border-t border-th-bkg-4 pt-3">
<p>{t('total')}</p>
<p className="pl-4 font-mono text-th-fgd-2">
{formatCurrencyValue(total)}
</p>
</div>
</div>
)
}
return null
}
return (
<div className="flex flex-col items-center pt-4 md:flex-row md:space-x-4">
{data.length ? (
<PieChart width={size} height={size}>
<Tooltip
cursor={{
fill: 'var(--bkg-2)',
opacity: 0.5,
}}
content={<CustomTooltip contributions={data} />}
position={{ x: 88, y: 0 }}
/>
<Pie
cursor="pointer"
data={data}
dataKey="contribution"
cx="50%"
cy="50%"
outerRadius={outerRadius}
innerRadius={innerRadius}
minAngle={2}
startAngle={90}
endAngle={450}
>
{data.map((entry, index) => {
const fillColor = entry.isAsset
? COLORS.UP[theme]
: COLORS.DOWN[theme]
return (
<Cell
key={`cell-${index}`}
fill={fillColor}
stroke={COLORS.BKG1[theme]}
strokeWidth={1}
/>
)
})}
</Pie>
</PieChart>
) : (
<div className="h-20 w-20 rounded-full ring-[8px] ring-inset ring-th-bkg-3" />
)}
</div>
)
}
export default HealthContributionsChart

11
public/icons/hnt.svg Normal file
View File

@ -0,0 +1,11 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_11_348)">
<circle cx="16" cy="16" r="12" fill="white"/>
<path d="M20.2611 8.32332C21.1896 7.39482 22.715 7.39482 23.6435 8.32332C24.572 9.25181 24.572 10.7772 23.6435 11.7057C23.0964 12.2528 22.3668 12.485 21.6207 12.3855C21.5876 12.3855 21.5544 12.3855 21.5047 12.3855C21.2891 12.3523 21.057 12.3855 20.8249 12.485C20.5098 12.6342 20.2943 12.8829 20.1782 13.1813C20.0622 13.4798 20.0788 13.8114 20.2114 14.1098C21.0073 15.8342 20.6425 17.9233 19.2829 19.2663C17.9233 20.6259 15.8508 20.9907 14.1264 20.1948C13.8114 20.0456 13.4798 20.0456 13.1813 20.1617C12.8829 20.2777 12.6342 20.4933 12.5016 20.7917C12.4187 20.9907 12.3689 21.1896 12.3855 21.4052C12.3855 21.4383 12.3855 21.4881 12.3855 21.5212C12.5181 22.2839 12.2528 23.0632 11.7223 23.6104C10.7938 24.5389 9.26839 24.5389 8.3399 23.6104C7.89223 23.1627 7.64352 22.5658 7.64352 21.9192C7.64352 21.2891 7.89223 20.6756 8.3399 20.228C8.88705 19.6808 9.61658 19.4487 10.3627 19.5482C10.3793 19.5482 10.3793 19.5482 10.3959 19.5482C10.4788 19.5648 10.5617 19.5813 10.6446 19.5813C10.8269 19.5813 10.9927 19.5482 11.1585 19.4653C11.4736 19.3161 11.6891 19.0674 11.7886 18.7855C11.9047 18.487 11.9047 18.1389 11.7554 17.8238C10.9596 16.0829 11.3244 14.0104 12.6839 12.6674C14.0435 11.3078 16.1161 10.943 17.8404 11.7389C18.1389 11.8881 18.487 11.8881 18.7855 11.772C19.0839 11.656 19.3326 11.4404 19.4819 11.142C19.5979 10.9098 19.6145 10.6446 19.5813 10.3959C19.4653 9.64974 19.714 8.87047 20.2611 8.32332ZM17.8902 17.8736C18.9181 16.8456 18.9181 15.171 17.8902 14.1264C16.8622 13.0984 15.1876 13.0984 14.143 14.1264C13.115 15.1544 13.115 16.829 14.143 17.8736C15.1876 18.9016 16.8622 18.9016 17.8902 17.8736ZM16 0C24.8373 0 32 7.16269 32 16C32 24.8373 24.8373 32 16 32C7.16269 32 0 24.8373 0 16C0 7.16269 7.16269 0 16 0ZM24.5223 12.601C25.9316 11.1917 25.9316 8.90363 24.5223 7.47772C23.113 6.06839 20.8249 6.06839 19.399 7.47772C18.8684 8.00829 18.5534 8.65492 18.4041 9.31813C15.8342 8.35648 12.8995 8.96995 10.943 10.9264C8.98653 12.8829 8.37306 15.8342 9.3513 18.4041C8.6715 18.5368 8.02487 18.8684 7.4943 19.399C6.08497 20.8083 6.08497 23.0964 7.4943 24.5223C8.90363 25.9316 11.1917 25.9316 12.6176 24.5223C13.1482 23.9917 13.4798 23.3285 13.6124 22.6487C14.4083 22.9472 15.2373 23.0964 16.0663 23.0964C17.9067 23.0964 19.714 22.3834 21.057 21.0404C23.0135 19.0839 23.6269 16.1658 22.6819 13.6124C23.3451 13.4466 23.9917 13.115 24.5223 12.601Z" fill="#38A2FF"/>
</g>
<defs>
<clipPath id="clip0_11_348">
<rect width="32" height="32" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -110,6 +110,7 @@ export const CUSTOM_TOKEN_ICONS: { [key: string]: boolean } = {
dual: true,
eth: true,
'eth (portal)': true,
hnt: true,
jitosol: true,
ldo: true,
mngo: true,