Skip to content

Commit

Permalink
Merge pull request #1236 from oasisprotocol/mz/validatorsDetails-sign…
Browse files Browse the repository at this point in the history
…edBlocks

Validator details cards placeholders
  • Loading branch information
buberdds authored Mar 8, 2024
2 parents c7cd094 + 6eb3a8c commit 965365d
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 0 deletions.
1 change: 1 addition & 0 deletions .changelog/1236.trivial.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Validator details cards placeholders
64 changes: 64 additions & 0 deletions src/app/components/BlockStats/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { ReactNode } from 'react'
import Box from '@mui/material/Box'
import Tooltip from '@mui/material/Tooltip'
import { styled } from '@mui/material/styles'
import { COLORS } from '../../../styles/theme/colors'

const StyledSquare = styled(Box, {
shouldForwardProp: prop => prop !== 'success',
})(({ success }: { success?: boolean }) => {
return {
display: 'flex',
width: '24px',
height: '24px',
borderRadius: '3px',
backgroundColor: success ? COLORS.eucalyptus : COLORS.errorIndicatorBackground,
}
})

type BlockStatsProps<T> = {
data: T[]
dataKey: Extract<keyof T, string>
legendLabels?: {
success: string
fail: string
}
tooltipFormatter?: (data: string) => ReactNode
}

export const BlockStats = <T extends { [key: string]: any }>({
data,
dataKey,
legendLabels,
tooltipFormatter,
}: BlockStatsProps<T>) => {
const statusKey = Object.keys(data[0]).find(key => key !== dataKey)
if (!statusKey) {
throw new Error('Not able to determine status key')
}

return (
<>
<Box sx={{ display: 'flex', flexWrap: 'wrap' }} gap={2}>
{data.map(item => {
const title = tooltipFormatter ? tooltipFormatter(item[dataKey].toString()) : item[dataKey]
return (
<Tooltip key={item[dataKey]} title={title} placement="top">
<StyledSquare success={item[statusKey]} />
</Tooltip>
)
})}
</Box>
{legendLabels && (
<Box pt={5} sx={{ display: 'flex' }}>
<Box gap={3} mr={4} sx={{ display: 'flex' }}>
<StyledSquare success /> {legendLabels.success}
</Box>
<Box gap={3} sx={{ display: 'flex' }}>
<StyledSquare /> {legendLabels.fail}
</Box>
</Box>
)}
</>
)
}
17 changes: 17 additions & 0 deletions src/app/pages/ValidatorDetailsPage/ProposedBlocks.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
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 { SearchScope } from 'types/searchScope'

export const ProposedBlocks: FC<{ scope: SearchScope }> = ({ scope }) => {
const { t } = useTranslation()

return (
<Card>
<CardHeader disableTypography component="h3" title={t('validator.proposedBlocks')} />
<CardContent>{/* TODO: add proposed block paginated list when API is ready */}</CardContent>
</Card>
)
}
47 changes: 47 additions & 0 deletions src/app/pages/ValidatorDetailsPage/SignedBlocks.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
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 Typography from '@mui/material/Typography'
import { COLORS } from 'styles/theme/colors'
import { BlockStats } from '../../components/BlockStats'

export const SignedBlocks: FC = () => {
const { t } = useTranslation()
// TODO: provide data from the API and sync dataKey value
const data: any[] = []
const dataKey = ''

return (
<Card sx={{ flex: 1 }}>
<CardHeader
disableTypography
component="h3"
title={t('validator.signedBlocks')}
sx={{ paddingBottom: 0 }}
/>
<CardContent>
{data && data.length > 0 && (
<>
<Typography
sx={{
fontSize: '18px',
color: COLORS.grayMedium,
paddingBottom: 4,
}}
>
{t('validator.signedBlocksDescription')}
</Typography>
<BlockStats
data={data}
dataKey={dataKey}
legendLabels={{ success: t('validator.signed'), fail: t('validator.missed') }}
tooltipFormatter={value => t('validator.blockWithHeight', { height: value })}
/>
</>
)}
</CardContent>
</Card>
)
}
42 changes: 42 additions & 0 deletions src/app/pages/ValidatorDetailsPage/StakingTrend.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
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 { LineChart } from '../../components/charts/LineChart'
import { SearchScope } from 'types/searchScope'
import { useScreenSize } from '../../hooks/useScreensize'

export const StakingTrend: FC<{ scope: SearchScope }> = ({ scope }) => {
const { t } = useTranslation()
const { isMobile } = useScreenSize()
// TODO: provide data from the API is ready and sync dataKey value
const windows = undefined

return (
<Card sx={{ flex: 1 }}>
<CardHeader disableTypography component="h3" title={t('validator.stakingTrend')} />
<CardContent sx={{ height: 250 }}>
{windows && (
<LineChart
tooltipActiveDotRadius={9}
cartesianGrid
strokeWidth={3}
dataKey={'volume'}
data={windows}
margin={{ bottom: 16, top: isMobile ? 0 : 16 }}
tickMargin={16}
withLabels
formatters={{
data: (value: number) => value.toLocaleString(),
label: (value: string) =>
t('common.formattedDateTime', {
timestamp: new Date(value),
}),
}}
/>
)}
</CardContent>
</Card>
)
}
20 changes: 20 additions & 0 deletions src/app/pages/ValidatorDetailsPage/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { FC } from 'react'
import { styled } from '@mui/material/styles'
import { useTranslation } from 'react-i18next'
import { useHref, useLoaderData } from 'react-router-dom'
import Card from '@mui/material/Card'
import CardContent from '@mui/material/CardContent'
import Divider from '@mui/material/Divider'
import Grid from '@mui/material/Grid'
import { useScreenSize } from '../../hooks/useScreensize'
import { Validator, useGetConsensusValidatorsEntityId } from '../../../oasis-nexus/api'
import { RouterTabs } from '../../components/RouterTabs'
Expand All @@ -18,8 +20,17 @@ import { ValidatorTitleCard } from './ValidatorTitleCard'
import { useRequiredScopeParam } from 'app/hooks/useScopeParam'
import { AddressLoaderData } from 'app/utils/route-utils'
import { ValidatorSnapshot } from './ValidatorSnapshot'
import { SignedBlocks } from './SignedBlocks'
import { StakingTrend } from './StakingTrend'
import { ProposedBlocks } from './ProposedBlocks'
import { ValidatorDetailsContext } from './hooks'

export const StyledGrid = styled(Grid)(({ theme }) => ({
[theme.breakpoints.up('sm')]: {
display: 'flex',
},
}))

export const ValidatorDetailsPage: FC = () => {
const { t } = useTranslation()
const { isMobile } = useScreenSize()
Expand All @@ -38,6 +49,15 @@ export const ValidatorDetailsPage: FC = () => {
<ValidatorSnapshot scope={scope} validator={validator} />
<Divider variant="layout" sx={{ mt: isMobile ? 4 : 0 }} />
<ValidatorDetailsCard isLoading={isLoading} validator={validator} />
<Grid container spacing={4}>
<StyledGrid item xs={12} md={6}>
<StakingTrend scope={scope} />
</StyledGrid>
<StyledGrid item xs={12} md={6}>
<SignedBlocks />
</StyledGrid>
</Grid>
<ProposedBlocks scope={scope} />
<RouterTabs tabs={[{ label: t('common.transactions'), to: transactionsLink }]} context={context} />
</PageLayout>
)
Expand Down
7 changes: 7 additions & 0 deletions src/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,7 @@
"validator": {
"active": "Active",
"balanceDistribution": "Balance Distribution",
"blockWithHeight": "Block {{height}}",
"boundsNotSet": "No bounds set (0% - 100%)",
"change": "Change (24h)",
"commission": "Commission",
Expand All @@ -600,10 +601,16 @@
"groupStatus": "{{status}} validators",
"inactive": "Inactive",
"listTitle": "Validators",
"missed": "Missed",
"nodeId": "Node ID",
"participationRate": "Participation Rate",
"signed": "Signed",
"signedBlocks": "Signed Blocks",
"signedBlocksDescription": "Last 100 blocks",
"proposedBlocks": "Proposed Blocks",
"snapshot": "Validator Snapshot",
"staked": "Staked",
"stakingTrend": "Staking Trend",
"startDate": "Start Date",
"title": "Validator",
"totalShare": "Total Share",
Expand Down

0 comments on commit 965365d

Please sign in to comment.