Skip to content

Commit

Permalink
Merge pull request #1261 from oasisprotocol/mz/consensusAccCards
Browse files Browse the repository at this point in the history
Consensus account details cards
  • Loading branch information
buberdds authored Mar 8, 2024
2 parents 965365d + b81651a commit 0593cea
Show file tree
Hide file tree
Showing 20 changed files with 400 additions and 67 deletions.
1 change: 1 addition & 0 deletions .changelog/1261.trivial.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add secondary cards to Consensus account details page
2 changes: 1 addition & 1 deletion src/app/components/AccountList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const AccountList: FC<AccountListProps> = ({ isLoading, limit, pagination
{ key: 'address', content: t('common.address') },
...(verbose ? [{ key: 'creationDate', content: t('account.birth') }] : []),
{ align: TableCellAlign.Right, key: 'available', content: t('account.available') },
{ align: TableCellAlign.Right, key: 'staked', content: t('account.staked') },
{ align: TableCellAlign.Right, key: 'staked', content: t('common.staked') },
{ align: TableCellAlign.Right, key: 'debonding', content: t('account.debonding') },
{ align: TableCellAlign.Right, key: 'total', content: <strong>{t('account.totalBalance')}</strong> },
]
Expand Down
56 changes: 56 additions & 0 deletions src/app/components/Delegations/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import { Table, TableCellAlign, TableColProps } from '../../components/Table'
import { Delegation } from '../../../oasis-nexus/api'
import { TablePaginationProps } from '../Table/TablePagination'
import { RoundedBalance } from '../RoundedBalance'
import { ValidatorLink } from '../Validators/ValidatorLink'

type DelegationsProps = {
delegations?: Delegation[]
isLoading: boolean
limit: number
pagination: false | TablePaginationProps
}

export const Delegations: FC<DelegationsProps> = ({ delegations, isLoading, limit, pagination }) => {
const { t } = useTranslation()

const tableColumns: TableColProps[] = [
{ key: 'name', content: t('validator.title') },
{ align: TableCellAlign.Right, key: 'shares', content: t('validator.shares') },
{ align: TableCellAlign.Right, key: 'staked', content: t('common.staked') },
]
const tableRows = delegations?.map(delegation => ({
key: delegation.validator,
data: [
{
// TODO: Use trimmed name when API is updated
content: <ValidatorLink address={delegation.validator} alwaysTrim network={delegation.network} />,
key: 'name',
},
{
align: TableCellAlign.Right,
content: <RoundedBalance value={delegation.shares} />,
key: 'shares',
},
{
align: TableCellAlign.Right,

content: <RoundedBalance value={delegation.amount} ticker={delegation.ticker} />,
key: 'staked',
},
],
}))

return (
<Table
columns={tableColumns}
rows={tableRows}
rowsNumber={limit}
name={t('common.staking')}
isLoading={isLoading}
pagination={pagination}
/>
)
}
22 changes: 18 additions & 4 deletions src/app/components/Validators/ValidatorLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,18 @@ type ValidatorLinkProps = {
address: string
name?: string
network: Network
alwaysTrim?: boolean
}

export const ValidatorLink: FC<ValidatorLinkProps> = ({ address, name, network }) => {
export const ValidatorLink: FC<ValidatorLinkProps> = ({ address, name, network, alwaysTrim }) => {
const { isTablet } = useScreenSize()
const to = RouteUtils.getValidatorRoute(network, address)
return (
<Typography variant="mono" component="span" sx={{ color: COLORS.brandDark, fontWeight: 700 }}>
{isTablet ? (
<TabletValidatorLink address={address} name={name} to={to} />
) : (
<Link component={RouterLink} to={to}>
{name || address}
</Link>
<DesktopValidatorLink address={address} alwaysTrim={alwaysTrim} name={name} to={to} />
)}
</Typography>
)
Expand All @@ -51,3 +50,18 @@ const TabletValidatorLink: FC<TabletValidatorLinkProps> = ({ address, name, to }
}
return <TrimLinkLabel label={address} to={to} />
}

type DesktopValidatorLinkProps = TabletValidatorLinkProps & {
alwaysTrim?: boolean
}

const DesktopValidatorLink: FC<DesktopValidatorLinkProps> = ({ address, name, to, alwaysTrim }) => {
if (alwaysTrim) {
return <TrimLinkLabel label={address} to={to} />
}
return (
<Link component={RouterLink} to={to}>
{name || address}
</Link>
)
}
2 changes: 1 addition & 1 deletion src/app/components/Validators/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const Validators: FC<ValidatorsProps> = ({ isLoading, limit, pagination,
{ key: 'name', content: t('validator.title') },
{ align: TableCellAlign.Right, key: 'cumulativeVoting', content: t('validator.cumulativeVoting') },
{ align: TableCellAlign.Right, key: 'voting', content: t('validator.voting') },
{ align: TableCellAlign.Right, key: 'staked', content: t('validator.staked') },
{ align: TableCellAlign.Right, key: 'staked', content: t('common.staked') },
{ align: TableCellAlign.Right, key: 'change', content: t('validator.change') },
{ align: TableCellAlign.Right, key: 'delegators', content: t('validator.delegators') },
{ align: TableCellAlign.Right, key: 'commission', content: t('validator.commission') },
Expand Down
97 changes: 97 additions & 0 deletions src/app/pages/ConsensusAccountDetailsPage/BalanceDistribution.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import Box from '@mui/material/Box'
import Card from '@mui/material/Card'
import CardHeader from '@mui/material/CardHeader'
import CardContent from '@mui/material/CardContent'
import Skeleton from '@mui/material/Skeleton'
import Typography from '@mui/material/Typography'
import { Account } from '../../../oasis-nexus/api'
import { PieChart } from '../../components/charts/PieChart'
import { useScreenSize } from '../../hooks/useScreensize'
import { getPreciseNumberFormat } from 'locales/getPreciseNumberFormat'
import { COLORS } from '../../../styles/theme/colors'
import { ConsensusAccountCardEmptyState } from './ConsensusAccountCardEmptyState'

type BalanceDistributionProps = {
account: Account | undefined
isLoading: boolean
}

export const BalanceDistribution: FC<BalanceDistributionProps> = ({ account, isLoading }) => {
const { t } = useTranslation()

return (
<Card sx={{ height: '100%' }}>
<CardHeader disableTypography component="h3" title={t('account.balanceDistribution')} />
<CardContent>
{isLoading && <Skeleton variant="rectangular" height={300} />}
{account && <BalanceDistributionContent account={account} />}
</CardContent>
</Card>
)
}

type BalanceDistributionContentProps = {
account: Account
}

const BalanceDistributionContent: FC<BalanceDistributionContentProps> = ({ account }) => {
const { t } = useTranslation()
const { isMobile } = useScreenSize()
const totalValue = t('common.valueInToken', {
...getPreciseNumberFormat(account.total),
ticker: account.ticker,
})

if (account.total === '0') {
return <ConsensusAccountCardEmptyState label={t('account.noBalances')} />
}

const data = [
{
label: t('account.available'),
value: Number(account.available),
},
{
label: t('common.staking'),
value: Number(account.delegations_balance),
},
{
label: t('account.debonding'),
value: Number(account.debonding_delegations_balance),
},
]

return (
<>
<Typography
sx={{
fontSize: isMobile ? '14px' : '24px',
fontWeight: isMobile ? 500 : 700,
color: COLORS.brandDark,
mb: 3,
}}
>
{t('account.totalValue', {
value: totalValue,
})}
</Typography>
<Box sx={{ height: '250px' }}>
<PieChart
compact={false}
data={data}
dataKey="value"
formatters={{
data: (value: number) =>
t('common.valueInToken', {
...getPreciseNumberFormat(value.toString()),
ticker: account.ticker,
}),
label: (label: string) => label,
}}
/>
</Box>
</>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { FC, ReactNode } from 'react'
import Box from '@mui/material/Box'
import Typography from '@mui/material/Typography'
import { styled } from '@mui/material/styles'
import StackedBarChartIcon from '@mui/icons-material/StackedBarChart'
import { COLORS } from '../../../styles/theme/colors'

const StyledBox = styled(Box)(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
[theme.breakpoints.down('sm')]: {
minHeight: '150px',
paddingTop: theme.spacing(3),
},
[theme.breakpoints.up('sm')]: {
minHeight: '200px',
paddingTop: theme.spacing(6),
},
}))

type ConsensusAccountCardEmptyStateProps = {
children?: ReactNode
label: string
}

export const ConsensusAccountCardEmptyState: FC<ConsensusAccountCardEmptyStateProps> = ({
children,
label,
}) => {
return (
<StyledBox gap={3}>
<StackedBarChartIcon sx={{ color: COLORS.grayMedium, fontSize: 40, opacity: 0.5 }} />
<Typography
sx={{
color: COLORS.grayMedium,
fontWeight: 700,
maxWidth: '170px',
textAlign: 'center',
opacity: 0.5,
}}
>
{label}
</Typography>
{children}
</StyledBox>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ const ConsensusAccountDetails: FC<ConsensusAccountDetailsCardProps> = ({ account
ticker: account.ticker,
})}
</dd>
<StyledListTitle>{t('account.staking')}</StyledListTitle>
<StyledListTitle>{t('common.staking')}</StyledListTitle>
<dd>
{t('common.valueInToken', {
...getPreciseNumberFormat(account.delegations_balance!),
Expand Down
95 changes: 95 additions & 0 deletions src/app/pages/ConsensusAccountDetailsPage/Staking.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import Card from '@mui/material/Card'
import CardHeader from '@mui/material/CardHeader'
import CardContent from '@mui/material/CardContent'
import Link from '@mui/material/Link'
import Skeleton from '@mui/material/Skeleton'
import { COLORS } from '../../../styles/theme/colors'
import { Account, useGetConsensusAccountsAddressDelegations } from '../../../oasis-nexus/api'
import { useRequiredScopeParam } from '../../../app/hooks/useScopeParam'
import { AppErrors } from '../../../types/errors'
import { NUMBER_OF_ITEMS_ON_DASHBOARD as PAGE_SIZE } from '../../config'
import { useSearchParamsPagination } from '../../components/Table/useSearchParamsPagination'
import { Delegations } from '../..//components/Delegations'
import { docs, wallet } from '../../utils/externalLinks'
import { t } from 'i18next'
import { ConsensusAccountCardEmptyState } from './ConsensusAccountCardEmptyState'

type StakingProps = {
account: Account | undefined
isLoading: boolean
}

export const Staking: FC<StakingProps> = ({ account, isLoading }) => {
const { t } = useTranslation()

return (
<Card sx={{ height: '100%' }}>
<CardHeader
action={
<Link
href={docs.consensusStaking}
rel="noopener noreferrer"
target="_blank"
sx={{ color: COLORS.brandDark }}
>
{t('validator.sharesDocs')}
</Link>
}
disableTypography
component="h3"
title={t('common.staking')}
/>
<CardContent>
{isLoading && <Skeleton variant="rectangular" height={300} />}
{account && <StakingContent address={account?.address} />}
</CardContent>
</Card>
)
}

type StakingContentProps = {
address: string
}

const StakingContent: FC<StakingContentProps> = ({ address }) => {
const pagination = useSearchParamsPagination('page')
const offset = (pagination.selectedPage - 1) * PAGE_SIZE
const scope = useRequiredScopeParam()
const { network } = scope
const delegationsQuery = useGetConsensusAccountsAddressDelegations(network, address)
const { isLoading, isFetched, data } = delegationsQuery
if (isFetched && offset && !delegationsQuery.data?.data?.delegations?.length) {
throw AppErrors.PageDoesNotExist
}

if (isFetched && !delegationsQuery.data?.data.delegations.length) {
return (
<ConsensusAccountCardEmptyState label={t('account.notStaking')}>
<Link href={wallet.homepage} rel="noopener noreferrer" target="_blank">
{t('account.startStaking')}
</Link>
</ConsensusAccountCardEmptyState>
)
}

return (
<>
{isFetched && (
<Delegations
delegations={delegationsQuery.data?.data.delegations}
isLoading={isLoading}
limit={PAGE_SIZE}
pagination={{
selectedPage: pagination.selectedPage,
linkToPage: pagination.linkToPage,
totalCount: data?.data.total_count,
isTotalCountClipped: data?.data.is_total_count_clipped,
rowsPerPage: PAGE_SIZE,
}}
/>
)}
</>
)
}
Loading

0 comments on commit 0593cea

Please sign in to comment.