Update tooltips for routing and average score (#3133)
* update tooltips for routing and average score * fix up table alignment fix lint errors * add node_performance to explorer api response for mixnodes * use mixnode node_performance for avg and lastest values * move stake sat to top table fix lint errors * update stake saturation text color
This commit is contained in:
@@ -11,7 +11,7 @@ use serde::Serialize;
|
||||
use std::sync::Arc;
|
||||
use std::time::SystemTime;
|
||||
use tokio::sync::RwLock;
|
||||
use validator_client::models::SelectionChance;
|
||||
use validator_client::models::{NodePerformance, SelectionChance};
|
||||
|
||||
#[derive(Clone, Debug, Serialize, JsonSchema, PartialEq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
@@ -36,6 +36,7 @@ pub(crate) struct PrettyDetailedMixNodeBond {
|
||||
pub stake_saturation: f32,
|
||||
pub uncapped_saturation: f32,
|
||||
pub avg_uptime: u8,
|
||||
pub node_performance: NodePerformance,
|
||||
pub estimated_operator_apy: f64,
|
||||
pub estimated_delegators_apy: f64,
|
||||
pub operating_cost: Coin,
|
||||
|
||||
@@ -153,6 +153,7 @@ impl ThreadsafeMixNodesCache {
|
||||
layer: node.mixnode_details.bond_information.layer,
|
||||
mix_node: node.mixnode_details.bond_information.mix_node.clone(),
|
||||
avg_uptime: node.performance.round_to_integer(),
|
||||
node_performance: node.node_performance.clone(),
|
||||
stake_saturation: best_effort_small_dec_to_f64(node.stake_saturation) as f32,
|
||||
uncapped_saturation: best_effort_small_dec_to_f64(node.uncapped_stake_saturation)
|
||||
as f32,
|
||||
|
||||
@@ -62,6 +62,7 @@ export class Api {
|
||||
if (cachedMixnodes) {
|
||||
return cachedMixnodes;
|
||||
}
|
||||
|
||||
const res = await fetch(MIXNODES_API);
|
||||
const json = await res.json();
|
||||
storeInCache('mixnodes', JSON.stringify(json));
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
import * as React from 'react';
|
||||
import { Link, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from '@mui/material';
|
||||
import {
|
||||
Link,
|
||||
Paper,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
TableCellProps,
|
||||
} from '@mui/material';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { Tooltip } from '@nymproject/react/tooltip/Tooltip';
|
||||
import { CopyToClipboard } from '@nymproject/react/clipboard/CopyToClipboard';
|
||||
@@ -8,13 +18,13 @@ import { cellStyles } from './Universal-DataGrid';
|
||||
import { unymToNym } from '../utils/currency';
|
||||
import { GatewayEnrichedRowType } from './Gateways';
|
||||
import { MixnodeRowType } from './MixNodes';
|
||||
import { StakeSaturationProgressBar } from './MixNodes/Economics/StakeSaturationProgressBar';
|
||||
|
||||
export type ColumnsType = {
|
||||
field: string;
|
||||
title: string;
|
||||
headerAlign: string;
|
||||
flex?: number;
|
||||
width?: number;
|
||||
headerAlign?: TableCellProps['align'];
|
||||
width?: string | number;
|
||||
tooltipInfo?: string;
|
||||
};
|
||||
|
||||
@@ -50,6 +60,10 @@ function formatCellValues(val: string | number, field: string) {
|
||||
);
|
||||
}
|
||||
|
||||
if (field === 'stake_saturation') {
|
||||
return <StakeSaturationProgressBar value={Number(val)} threshold={100} />;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
@@ -61,11 +75,11 @@ export const DetailTable: FCWithChildren<{
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<TableContainer component={Paper}>
|
||||
<Table sx={{ minWidth: 650 }} aria-label={tableName}>
|
||||
<Table sx={{ minWidth: 1080 }} aria-label={tableName}>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
{columnsData?.map(({ field, title, flex, tooltipInfo }) => (
|
||||
<TableCell key={field} sx={{ fontSize: 14, fontWeight: 600, flex }}>
|
||||
{columnsData?.map(({ field, title, width, tooltipInfo }) => (
|
||||
<TableCell key={field} sx={{ fontSize: 14, fontWeight: 600, width }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
{tooltipInfo && (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
|
||||
@@ -4,57 +4,48 @@ export const EconomicsInfoColumns: ColumnsType[] = [
|
||||
{
|
||||
field: 'estimatedTotalReward',
|
||||
title: 'Estimated Total Reward',
|
||||
flex: 1,
|
||||
headerAlign: 'left',
|
||||
width: '15%',
|
||||
tooltipInfo:
|
||||
'Estimated node reward (total for the operator and delegators) in the current epoch. There are roughly 24 epochs in a day.',
|
||||
},
|
||||
{
|
||||
field: 'estimatedOperatorReward',
|
||||
title: 'Estimated Operator Reward',
|
||||
flex: 1,
|
||||
headerAlign: 'left',
|
||||
width: '15%',
|
||||
tooltipInfo:
|
||||
"Estimated operator's reward (including PM and Operating Cost) in the current epoch. There are roughly 24 epochs in a day.",
|
||||
},
|
||||
{
|
||||
field: 'selectionChance',
|
||||
title: 'Active Set Probability',
|
||||
flex: 1,
|
||||
headerAlign: 'left',
|
||||
width: '12.5%',
|
||||
tooltipInfo:
|
||||
'Probability of getting selected in the reward set (active and standby nodes) in the next epoch. The more your stake, the higher the chances to be selected.',
|
||||
},
|
||||
{
|
||||
field: 'stakeSaturation',
|
||||
title: 'Stake Saturation',
|
||||
flex: 1,
|
||||
headerAlign: 'left',
|
||||
tooltipInfo:
|
||||
'Level of stake saturation for this node. Nodes receive more rewards the higher their saturation level, up to 100%. Beyond 100% no additional rewards are granted. The current stake saturation level is 730k NYM, computed as S/K where S is target amount of tokens staked in the network and K is the number of nodes in the reward set.',
|
||||
},
|
||||
{
|
||||
field: 'profitMargin',
|
||||
title: 'Profit Margin',
|
||||
flex: 1,
|
||||
headerAlign: 'left',
|
||||
width: '12.5%',
|
||||
tooltipInfo:
|
||||
'Percentage of the delegators rewards that the operator takes as fee before rewards are distributed to the delegators.',
|
||||
},
|
||||
{
|
||||
field: 'operatingCost',
|
||||
title: 'Operating Cost',
|
||||
flex: 1,
|
||||
headerAlign: 'left',
|
||||
width: '10%',
|
||||
tooltipInfo:
|
||||
'Monthly operational cost of running this node. This cost is set by the operator and it influences how the rewards are split between the operator and delegators.',
|
||||
},
|
||||
{
|
||||
field: 'avgUptime',
|
||||
title: 'Avg. Score',
|
||||
width: '10%',
|
||||
tooltipInfo: "Mixnode's average routing score in the last 24 hour",
|
||||
},
|
||||
{
|
||||
field: 'nodePerformance',
|
||||
title: 'Routing Score',
|
||||
flex: 1,
|
||||
headerAlign: 'left',
|
||||
tooltipInfo:
|
||||
'Node’s routing score is relative to that of the network. Each time a node is tested, the test packets have to go through the full path of the network (a gateway + 3 nodes). If a node in the path drop packets it will affect the score of other nodes in the test.',
|
||||
"Mixnode's most recent score (measured in the last 15 minutes). Routing score is relative to that of the network. Each time a gateway is tested, the test packets have to go through the full path of the network (gateway + 3 nodes). If a node in the path drop packets it will affect the score of the gateway and other nodes in the test.",
|
||||
},
|
||||
];
|
||||
|
||||
@@ -9,14 +9,14 @@ const parseToNumber = (value: number | undefined | string) =>
|
||||
export const EconomicsProgress: FCWithChildren<
|
||||
LinearProgressProps & {
|
||||
threshold?: number;
|
||||
color: string;
|
||||
}
|
||||
> = ({ threshold, ...props }) => {
|
||||
> = ({ threshold, color, ...props }) => {
|
||||
const theme = useTheme();
|
||||
const { value } = props;
|
||||
|
||||
const valueNumber: number = parseToNumber(value);
|
||||
const thresholdNumber: number = parseToNumber(threshold);
|
||||
const percentageColor = valueNumber > (threshold || 100) ? 'warning' : 'inherit';
|
||||
const percentageToDisplay = Math.min(valueNumber, thresholdNumber);
|
||||
|
||||
return (
|
||||
@@ -29,9 +29,9 @@ export const EconomicsProgress: FCWithChildren<
|
||||
<LinearProgress
|
||||
{...props}
|
||||
variant="determinate"
|
||||
color={percentageColor}
|
||||
color={color}
|
||||
value={percentageToDisplay}
|
||||
sx={{ width: '100%', borderRadius: '5px', backgroundColor: theme.palette.common.white }}
|
||||
sx={{ width: '100%', borderRadius: '5px' }}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -14,9 +14,7 @@ const row: EconomicsInfoRowWithIndex = {
|
||||
selectionChance: {
|
||||
value: 'High',
|
||||
},
|
||||
avgUptime: {
|
||||
value: '65 %',
|
||||
},
|
||||
|
||||
estimatedOperatorReward: {
|
||||
value: '80000.123456 NYM',
|
||||
},
|
||||
@@ -29,9 +27,11 @@ const row: EconomicsInfoRowWithIndex = {
|
||||
operatingCost: {
|
||||
value: '11121 NYM',
|
||||
},
|
||||
stakeSaturation: {
|
||||
value: '80 %',
|
||||
progressBarValue: 80,
|
||||
avgUptime: {
|
||||
value: '-',
|
||||
},
|
||||
nodePerformance: {
|
||||
value: '-',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -55,9 +55,7 @@ const emptyRow: EconomicsInfoRowWithIndex = {
|
||||
value: '-',
|
||||
progressBarValue: 0,
|
||||
},
|
||||
avgUptime: {
|
||||
value: '-',
|
||||
},
|
||||
|
||||
estimatedOperatorReward: {
|
||||
value: '-',
|
||||
},
|
||||
@@ -70,9 +68,11 @@ const emptyRow: EconomicsInfoRowWithIndex = {
|
||||
operatingCost: {
|
||||
value: '-',
|
||||
},
|
||||
stakeSaturation: {
|
||||
avgUptime: {
|
||||
value: '-',
|
||||
},
|
||||
nodePerformance: {
|
||||
value: '-',
|
||||
progressBarValue: 0,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -14,11 +14,15 @@ export const EconomicsInfoRows = (): EconomicsInfoRowWithIndex => {
|
||||
currencyToString((economicDynamicsStats?.data?.estimated_total_node_reward || '').toString()) || '-';
|
||||
const estimatedOperatorRewards =
|
||||
currencyToString((economicDynamicsStats?.data?.estimated_operator_reward || '').toString()) || '-';
|
||||
const stakeSaturation = economicDynamicsStats?.data?.uncapped_saturation || '-';
|
||||
const profitMargin = mixNode?.data?.profit_margin_percent
|
||||
? toPercentIntegerString(mixNode?.data?.profit_margin_percent)
|
||||
: '-';
|
||||
const avgUptime = economicDynamicsStats?.data?.current_interval_uptime;
|
||||
const avgUptime = mixNode?.data?.node_performance
|
||||
? toPercentIntegerString(mixNode?.data?.node_performance.last_24h)
|
||||
: '-';
|
||||
const nodePerformance = mixNode?.data?.node_performance
|
||||
? toPercentIntegerString(mixNode?.data?.node_performance.most_recent)
|
||||
: '-';
|
||||
|
||||
const opCost = mixNode?.data?.operating_cost;
|
||||
|
||||
@@ -33,10 +37,6 @@ export const EconomicsInfoRows = (): EconomicsInfoRowWithIndex => {
|
||||
selectionChance: {
|
||||
value: selectionChance(economicDynamicsStats),
|
||||
},
|
||||
stakeSaturation: {
|
||||
progressBarValue: typeof stakeSaturation === 'number' ? stakeSaturation * 100 : 0,
|
||||
value: typeof stakeSaturation === 'number' ? `${(stakeSaturation * 100).toFixed(2)} %` : '-',
|
||||
},
|
||||
profitMargin: {
|
||||
value: profitMargin ? `${profitMargin} %` : '-',
|
||||
},
|
||||
@@ -46,5 +46,8 @@ export const EconomicsInfoRows = (): EconomicsInfoRowWithIndex => {
|
||||
avgUptime: {
|
||||
value: avgUptime ? `${avgUptime} %` : '-',
|
||||
},
|
||||
nodePerformance: {
|
||||
value: nodePerformance,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import React from 'react';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import { useIsMobile } from '../../../hooks/useIsMobile';
|
||||
import { EconomicsProgress } from './EconomicsProgress';
|
||||
|
||||
export const StakeSaturationProgressBar = ({ value, threshold }: { value: number; threshold: number }) => {
|
||||
const isTablet = useIsMobile('lg');
|
||||
const percentageColor = value > (threshold || 100) ? 'warning' : 'inherit';
|
||||
const textColor = percentageColor === 'warning' ? 'warning.main' : 'nym.wallet.fee';
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{ display: 'flex', alignItems: 'center', flexDirection: isTablet ? 'column' : 'row' }}
|
||||
id="field"
|
||||
color={percentageColor}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
mr: isTablet ? 0 : 1,
|
||||
mb: isTablet ? 1 : 0,
|
||||
fontWeight: '600',
|
||||
fontSize: '12px',
|
||||
color: textColor,
|
||||
}}
|
||||
id="stake-saturation-progress-bar"
|
||||
>
|
||||
{value}%
|
||||
</Typography>
|
||||
<EconomicsProgress value={value} threshold={threshold} color={percentageColor} />
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -1,70 +1,19 @@
|
||||
import * as React from 'react';
|
||||
import { Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography } from '@mui/material';
|
||||
import { Box } from '@mui/system';
|
||||
import { useTheme, Theme } from '@mui/material/styles';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { Tooltip } from '@nymproject/react/tooltip/Tooltip';
|
||||
import { EconomicsRowsType, EconomicsInfoRowWithIndex } from './types';
|
||||
import { EconomicsProgress } from './EconomicsProgress';
|
||||
import { cellStyles } from '../../Universal-DataGrid';
|
||||
import { UniversalTableProps } from '../../DetailTable';
|
||||
import { useIsMobile } from '../../../hooks/useIsMobile';
|
||||
import { textColour } from '../../../utils';
|
||||
|
||||
const threshold = 100;
|
||||
|
||||
const textColour = (value: EconomicsRowsType, field: string, theme: Theme) => {
|
||||
const progressBarValue = value?.progressBarValue || 0;
|
||||
const fieldValue = value.value;
|
||||
|
||||
if (progressBarValue > 100) {
|
||||
return theme.palette.warning.main;
|
||||
}
|
||||
if (field === 'selectionChance') {
|
||||
// TODO: when v2 will be deployed, remove cases: VeryHigh, Moderate and VeryLow
|
||||
switch (fieldValue) {
|
||||
case 'High':
|
||||
case 'VeryHigh':
|
||||
return theme.palette.nym.networkExplorer.selectionChance.overModerate;
|
||||
case 'Good':
|
||||
case 'Moderate':
|
||||
return theme.palette.nym.networkExplorer.selectionChance.moderate;
|
||||
case 'Low':
|
||||
case 'VeryLow':
|
||||
return theme.palette.nym.networkExplorer.selectionChance.underModerate;
|
||||
default:
|
||||
return theme.palette.nym.wallet.fee;
|
||||
}
|
||||
}
|
||||
return theme.palette.nym.wallet.fee;
|
||||
};
|
||||
|
||||
const formatCellValues = (value: EconomicsRowsType, field: string) => {
|
||||
const isTablet = useIsMobile('lg');
|
||||
if (value.progressBarValue) {
|
||||
return (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', flexDirection: isTablet ? 'column' : 'row' }} id="field">
|
||||
<Typography
|
||||
sx={{
|
||||
mr: isTablet ? 0 : 1,
|
||||
mb: isTablet ? 1 : 0,
|
||||
fontWeight: '600',
|
||||
fontSize: '12px',
|
||||
}}
|
||||
id={field}
|
||||
>
|
||||
{value.value}
|
||||
</Typography>
|
||||
<EconomicsProgress threshold={threshold} value={value.progressBarValue} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }} id="field">
|
||||
<Typography sx={{ mr: 1, fontWeight: '600', fontSize: '12px' }} id={field}>
|
||||
{value.value}
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
const formatCellValues = (value: EconomicsRowsType, field: string) => (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }} id="field">
|
||||
<Typography sx={{ mr: 1, fontWeight: '600', fontSize: '12px' }} id={field}>
|
||||
{value.value}
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
|
||||
export const DelegatorsInfoTable: FCWithChildren<UniversalTableProps<EconomicsInfoRowWithIndex>> = ({
|
||||
tableName,
|
||||
@@ -78,21 +27,19 @@ export const DelegatorsInfoTable: FCWithChildren<UniversalTableProps<EconomicsIn
|
||||
<Table sx={{ minWidth: 650 }} aria-label={tableName}>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
{columnsData?.map(({ field, title, flex, tooltipInfo }) => (
|
||||
<TableCell key={field} sx={{ fontSize: 14, fontWeight: 600, flex }}>
|
||||
{columnsData?.map(({ field, title, tooltipInfo, width }) => (
|
||||
<TableCell key={field} sx={{ fontSize: 14, fontWeight: 600, width }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
{tooltipInfo && (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Tooltip
|
||||
title={tooltipInfo}
|
||||
id={field}
|
||||
placement="top-start"
|
||||
textColor={theme.palette.nym.networkExplorer.tooltip.color}
|
||||
bgColor={theme.palette.nym.networkExplorer.tooltip.background}
|
||||
maxWidth={230}
|
||||
arrow
|
||||
/>
|
||||
</Box>
|
||||
<Tooltip
|
||||
title={tooltipInfo}
|
||||
id={field}
|
||||
placement="top-start"
|
||||
textColor={theme.palette.nym.networkExplorer.tooltip.color}
|
||||
bgColor={theme.palette.nym.networkExplorer.tooltip.background}
|
||||
maxWidth={230}
|
||||
arrow
|
||||
/>
|
||||
)}
|
||||
{title}
|
||||
</Box>
|
||||
@@ -106,19 +53,11 @@ export const DelegatorsInfoTable: FCWithChildren<UniversalTableProps<EconomicsIn
|
||||
{columnsData?.map((_, index: number) => {
|
||||
const { field } = columnsData[index];
|
||||
const value: EconomicsRowsType = (eachRow as any)[field];
|
||||
|
||||
console.log(value);
|
||||
return (
|
||||
<TableCell
|
||||
key={_.title}
|
||||
component="th"
|
||||
scope="row"
|
||||
variant="body"
|
||||
sx={{
|
||||
...cellStyles,
|
||||
padding: 2,
|
||||
width: 200,
|
||||
fontSize: 12,
|
||||
fontWeight: 600,
|
||||
color: textColour(value, field, theme),
|
||||
}}
|
||||
data-testid={`${_.title.replace(/ /g, '-')}-value`}
|
||||
|
||||
@@ -3,14 +3,18 @@ export type EconomicsRowsType = {
|
||||
value: string;
|
||||
};
|
||||
|
||||
export interface EconomicsInfoRow {
|
||||
estimatedTotalReward: EconomicsRowsType;
|
||||
estimatedOperatorReward: EconomicsRowsType;
|
||||
selectionChance: EconomicsRowsType;
|
||||
stakeSaturation: EconomicsRowsType;
|
||||
profitMargin: EconomicsRowsType;
|
||||
avgUptime: EconomicsRowsType;
|
||||
operatingCost: EconomicsRowsType;
|
||||
}
|
||||
type TEconomicsInfoProperties =
|
||||
| 'estimatedTotalReward'
|
||||
| 'estimatedOperatorReward'
|
||||
| 'estimatedOperatorReward'
|
||||
| 'selectionChance'
|
||||
| 'profitMargin'
|
||||
| 'avgUptime'
|
||||
| 'nodePerformance'
|
||||
| 'operatingCost';
|
||||
|
||||
export type EconomicsInfoRow = {
|
||||
[k in TEconomicsInfoProperties]: EconomicsRowsType;
|
||||
};
|
||||
|
||||
export type EconomicsInfoRowWithIndex = EconomicsInfoRow & { id: number };
|
||||
|
||||
+6
-4
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable camelcase */
|
||||
import { MixNodeResponse, MixNodeResponseItem, MixnodeStatus } from '../../typeDefs/explorer-api';
|
||||
import { MixNodeResponse, MixNodeResponseItem, MixnodeStatus, NodePerformance } from '../../typeDefs/explorer-api';
|
||||
import { toPercentIntegerString } from '../../utils';
|
||||
import { unymToNym } from '../../utils/currency';
|
||||
|
||||
@@ -17,8 +17,9 @@ export type MixnodeRowType = {
|
||||
layer: string;
|
||||
profit_percentage: string;
|
||||
avg_uptime: string;
|
||||
stake_saturation: number;
|
||||
stake_saturation: React.ReactNode;
|
||||
operating_cost: string;
|
||||
node_performance: NodePerformance['most_recent'];
|
||||
};
|
||||
|
||||
export function mixnodeToGridRow(arrayOfMixnodes?: MixNodeResponse): MixnodeRowType[] {
|
||||
@@ -46,8 +47,9 @@ export function mixNodeResponseItemToMixnodeRowType(item: MixNodeResponseItem):
|
||||
host: item?.mix_node?.host || '',
|
||||
layer: item?.layer || '',
|
||||
profit_percentage: `${profitPercentage}%`,
|
||||
avg_uptime: `${item.avg_uptime}%` || '-',
|
||||
stake_saturation: uncappedSaturation,
|
||||
avg_uptime: `${toPercentIntegerString(item.node_performance.last_24h)}%`,
|
||||
stake_saturation: uncappedSaturation.toFixed(2),
|
||||
operating_cost: `${unymToNym(item.operating_cost?.amount, 6)} NYM`,
|
||||
node_performance: `${toPercentIntegerString(item.node_performance.most_recent)}%`,
|
||||
};
|
||||
}
|
||||
@@ -22,13 +22,11 @@ const columns: ColumnsType[] = [
|
||||
{
|
||||
field: 'bond',
|
||||
title: 'Bond',
|
||||
flex: 1,
|
||||
headerAlign: 'left',
|
||||
},
|
||||
{
|
||||
field: 'node_performance',
|
||||
title: 'Routing Score',
|
||||
flex: 1,
|
||||
headerAlign: 'left',
|
||||
tooltipInfo:
|
||||
"Gateway's most recent score (measured in the last 15 minutes). Routing score is relative to that of the network. Each time a gateway is tested, the test packets have to go through the full path of the network (gateway + 3 nodes). If a node in the path drop packets it will affect the score of the gateway and other nodes in the test",
|
||||
@@ -36,7 +34,6 @@ const columns: ColumnsType[] = [
|
||||
{
|
||||
field: 'avgUptime',
|
||||
title: 'Avg. Score',
|
||||
flex: 1,
|
||||
headerAlign: 'left',
|
||||
tooltipInfo: "Gateway's average routing score in the last 24 hours",
|
||||
},
|
||||
@@ -50,19 +47,16 @@ const columns: ColumnsType[] = [
|
||||
field: 'location',
|
||||
title: 'Location',
|
||||
headerAlign: 'left',
|
||||
flex: 1,
|
||||
},
|
||||
{
|
||||
field: 'owner',
|
||||
title: 'Owner',
|
||||
headerAlign: 'left',
|
||||
flex: 1,
|
||||
},
|
||||
{
|
||||
field: 'version',
|
||||
title: 'Version',
|
||||
headerAlign: 'left',
|
||||
flex: 1,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -17,45 +17,45 @@ const columns: ColumnsType[] = [
|
||||
{
|
||||
field: 'owner',
|
||||
title: 'Owner',
|
||||
headerAlign: 'left',
|
||||
width: 230,
|
||||
width: '15%',
|
||||
},
|
||||
{
|
||||
field: 'identity_key',
|
||||
title: 'Identity Key',
|
||||
headerAlign: 'left',
|
||||
width: 230,
|
||||
width: '15%',
|
||||
},
|
||||
|
||||
{
|
||||
field: 'bond',
|
||||
title: 'Stake',
|
||||
flex: 1,
|
||||
headerAlign: 'left',
|
||||
width: '12.5%',
|
||||
},
|
||||
{
|
||||
field: 'stake_saturation',
|
||||
title: 'Stake Saturation',
|
||||
width: '12.5%',
|
||||
tooltipInfo:
|
||||
'Level of stake saturation for this node. Nodes receive more rewards the higher their saturation level, up to 100%. Beyond 100% no additional rewards are granted. The current stake saturation level is 730k NYM, computed as S/K where S is target amount of tokens staked in the network and K is the number of nodes in the reward set.',
|
||||
},
|
||||
{
|
||||
field: 'self_percentage',
|
||||
width: '10%',
|
||||
title: 'Bond %',
|
||||
headerAlign: 'left',
|
||||
width: 99,
|
||||
},
|
||||
|
||||
{
|
||||
field: 'host',
|
||||
width: '10%',
|
||||
title: 'Host',
|
||||
headerAlign: 'left',
|
||||
flex: 1,
|
||||
},
|
||||
{
|
||||
field: 'location',
|
||||
title: 'Location',
|
||||
headerAlign: 'left',
|
||||
flex: 1,
|
||||
},
|
||||
|
||||
{
|
||||
field: 'layer',
|
||||
title: 'Layer',
|
||||
headerAlign: 'left',
|
||||
flex: 1,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -64,11 +64,10 @@ const columns: ColumnsType[] = [
|
||||
*/
|
||||
const PageMixnodeDetailWithState: FCWithChildren = () => {
|
||||
const { mixNode, mixNodeRow, description, stats, status, uptimeStory, uniqDelegations } = useMixnodeContext();
|
||||
|
||||
console.log(mixNodeRow);
|
||||
return (
|
||||
<Box component="main">
|
||||
<Title text="Mixnode Detail" />
|
||||
|
||||
<Grid container spacing={2} mt={1} mb={6}>
|
||||
<Grid item xs={12}>
|
||||
{mixNodeRow && description?.data && (
|
||||
@@ -76,13 +75,11 @@ const PageMixnodeDetailWithState: FCWithChildren = () => {
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<DetailTable columnsData={columns} tableName="Mixnode detail table" rows={mixNodeRow ? [mixNodeRow] : []} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Grid container spacing={2} mt={0}>
|
||||
<Grid item xs={12}>
|
||||
<DelegatorsInfoTable
|
||||
@@ -92,7 +89,6 @@ const PageMixnodeDetailWithState: FCWithChildren = () => {
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Grid container spacing={2} mt={0}>
|
||||
<Grid item xs={12}>
|
||||
<ContentCard title={`Stake Breakdown (${uniqDelegations?.data?.length} delegators)`}>
|
||||
@@ -100,7 +96,6 @@ const PageMixnodeDetailWithState: FCWithChildren = () => {
|
||||
</ContentCard>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Grid container spacing={2} mt={0}>
|
||||
<Grid item xs={12} md={4}>
|
||||
<ContentCard title="Mixnode Stats">
|
||||
@@ -144,7 +139,6 @@ const PageMixnodeDetailWithState: FCWithChildren = () => {
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Grid container spacing={2} mt={0}>
|
||||
<Grid item xs={12} md={4}>
|
||||
{status && (
|
||||
|
||||
@@ -168,7 +168,7 @@ export const PageMixnodes: FCWithChildren = () => {
|
||||
component={RRDLink}
|
||||
to={`/network-components/mixnode/${params.row.mix_id}`}
|
||||
>
|
||||
{`${params.value.toFixed(2)} %`}
|
||||
{`${params.value} %`}
|
||||
</MuiLink>
|
||||
),
|
||||
},
|
||||
@@ -238,13 +238,13 @@ export const PageMixnodes: FCWithChildren = () => {
|
||||
),
|
||||
},
|
||||
{
|
||||
field: 'avg_uptime',
|
||||
field: 'node_performance',
|
||||
headerName: 'Routing Score',
|
||||
disableColumnMenu: true,
|
||||
renderHeader: () => (
|
||||
<CustomColumnHeading
|
||||
headingTitle="Routing Score"
|
||||
tooltipInfo="Node’s routing score is relative to that of the network. Each time a node is tested, the test packets have to go through the full path of the network (a gateway + 3 nodes). If a node in the path drop packets it will affect the score of other nodes in the test."
|
||||
tooltipInfo="Mixnode's most recent score (measured in the last 15 minutes). Routing score is relative to that of the network. Each time a gateway is tested, the test packets have to go through the full path of the network (gateway + 3 nodes). If a node in the path drop packets it will affect the score of the gateway and other nodes in the test."
|
||||
/>
|
||||
),
|
||||
headerClassName: 'MuiDataGrid-header-override',
|
||||
@@ -332,7 +332,6 @@ export const PageMixnodes: FCWithChildren = () => {
|
||||
const handlePageSize = (event: SelectChangeEvent<string>) => {
|
||||
setPageSize(event.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title text="Mixnodes" />
|
||||
|
||||
@@ -84,6 +84,7 @@ export interface MixNodeResponseItem {
|
||||
};
|
||||
mix_node: MixNode;
|
||||
avg_uptime: number;
|
||||
node_performance: NodePerformance;
|
||||
stake_saturation: number;
|
||||
uncapped_saturation: number;
|
||||
operating_cost: Amount;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
/* eslint-disable camelcase */
|
||||
import { MutableRefObject } from 'react';
|
||||
import { Theme } from '@mui/material/styles';
|
||||
import { registerLocale, getName } from 'i18n-iso-countries';
|
||||
import { CountryData } from '../typeDefs/explorer-api';
|
||||
import { EconomicsRowsType } from '../components/MixNodes/Economics/types';
|
||||
|
||||
registerLocale(require('i18n-iso-countries/langs/en.json'));
|
||||
|
||||
@@ -53,3 +55,29 @@ export const splice = (start: number, deleteCount: number, address?: string): st
|
||||
* @returns A stringified integer
|
||||
*/
|
||||
export const toPercentIntegerString = (value: string) => Math.round(Number(value) * 100).toString();
|
||||
|
||||
export const textColour = (value: EconomicsRowsType, field: string, theme: Theme) => {
|
||||
const progressBarValue = value?.progressBarValue || 0;
|
||||
const fieldValue = value.value;
|
||||
|
||||
if (progressBarValue > 100) {
|
||||
return theme.palette.warning.main;
|
||||
}
|
||||
if (field === 'selectionChance') {
|
||||
// TODO: when v2 will be deployed, remove cases: VeryHigh, Moderate and VeryLow
|
||||
switch (fieldValue) {
|
||||
case 'High':
|
||||
case 'VeryHigh':
|
||||
return theme.palette.nym.networkExplorer.selectionChance.overModerate;
|
||||
case 'Good':
|
||||
case 'Moderate':
|
||||
return theme.palette.nym.networkExplorer.selectionChance.moderate;
|
||||
case 'Low':
|
||||
case 'VeryLow':
|
||||
return theme.palette.nym.networkExplorer.selectionChance.underModerate;
|
||||
default:
|
||||
return theme.palette.nym.wallet.fee;
|
||||
}
|
||||
}
|
||||
return theme.palette.nym.wallet.fee;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user