Skip to content
This repository has been archived by the owner on Jul 22, 2020. It is now read-only.

Commit

Permalink
feat: implement validator detail page
Browse files Browse the repository at this point in the history
  • Loading branch information
sunnygleason committed Jul 3, 2019
1 parent 110d4f6 commit fd7a9bc
Show file tree
Hide file tree
Showing 6 changed files with 307 additions and 5 deletions.
14 changes: 14 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import Bx2NavDrawer from './v2/Bx2NavDrawer';
import Bx2BlankComponent from './v2/Bx2BlankComponent';
import Bx2PanelValidatorsOverview from './v2/Bx2PanelValidatorsOverview';
import Bx2PanelValidators from './v2/Bx2PanelValidators';
import Bx2PanelValidatorDetail from './v2/Bx2PanelValidatorDetail';
import {stylesV2, themeV2} from './v2/ThemeV2';

const history = createBrowserHistory();
Expand All @@ -48,6 +49,9 @@ const Bx2PanelValidatorsOverviewThemed = withStyles(stylesV2)(
Bx2PanelValidatorsOverview,
);
const Bx2PanelValidatorsThemed = withStyles(stylesV2)(Bx2PanelValidators);
const Bx2PanelValidatorDetailThemed = withStyles(stylesV2)(
Bx2PanelValidatorDetail,
);
const Bx2BlankComponentThemed = withStyles(stylesV2)(Bx2BlankComponent);

class App extends Component {
Expand Down Expand Up @@ -708,6 +712,16 @@ class App extends Component {
<Bx2PanelValidatorsThemed nodes={this.state.nodes} />
)}
/>
<Route
path="/v2/validator/:id"
exact
render={({match}) => (
<Bx2PanelValidatorDetailThemed
nodes={this.state.nodes}
id={match.params.id}
/>
)}
/>
<Route
path="/v2/tourdesol"
exact
Expand Down
4 changes: 2 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import {BrowserRouter} from 'react-router-dom';

import './index.css';
import App from './App';
import AppV2 from './AppV2';
// import AppV2 from './AppV2';
import * as serviceWorker from './serviceWorker';

ReactDOM.render(
<BrowserRouter>
{window.location.pathname.includes('v2') ? <AppV2 /> : <App />}
{window.location.pathname.includes('v2') ? <App /> : <App />}
</BrowserRouter>,
document.getElementById('root'),
);
Expand Down
21 changes: 20 additions & 1 deletion src/v2/Bx2EntityLink.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,21 @@ class Bx2EntityLink extends React.Component {
);
}

renderValidatorId() {
return (
<Link
component={RouterLink}
to={'/v2/validator/' + this.props.validator_id}
color="secondary"
>
<code title={this.props.validator_id}>
{this.props.validator_id.substring(0, 22) +
(this.props.validator_id.length > 22 ? '\u2026' : '')}
</code>
</Link>
);
}

renderAccountId() {
return (
<code title={this.props.acct_id}>
Expand All @@ -72,7 +87,7 @@ class Bx2EntityLink extends React.Component {
}

render() {
const {node, ent, blk, txn, acct_id, prg_id} = this.props;
const {node, ent, blk, txn, validator_id, acct_id, prg_id} = this.props;

if (node) {
return this.renderNode();
Expand All @@ -90,6 +105,10 @@ class Bx2EntityLink extends React.Component {
return this.renderTransaction();
}

if (validator_id) {
return this.renderValidatorId();
}

if (acct_id) {
return this.renderAccountId();
}
Expand Down
269 changes: 269 additions & 0 deletions src/v2/Bx2PanelValidatorDetail.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
import React, {Component} from 'react';
import {
ComposableMap,
ZoomableGroup,
Geographies,
Geography,
Markers,
Marker,
} from 'react-simple-maps';
import {makeStyles} from '@material-ui/core/styles';
import BxEntityLink from './Bx2EntityLink';
import BxHelpLink from './Bx2HelpLink';
import Paper from '@material-ui/core/Paper';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow';
import Typography from '@material-ui/core/Typography';
import _ from 'lodash';
import PropTypes from 'prop-types';
import {Link as RouterLink} from 'react-router-dom';
import Link from '@material-ui/core/Link';
import Tooltip from '@material-ui/core/Tooltip';

function arrowGenerator(color) {
return {
'&[x-placement*="bottom"] $arrow': {
top: 0,
left: 0,
marginTop: '-0.95em',
width: '3em',
height: '1em',
'&::before': {
borderWidth: '0 1em 1em 1em',
borderColor: `transparent transparent ${color} transparent`,
},
},
'&[x-placement*="top"] $arrow': {
bottom: 0,
left: 0,
marginBottom: '-0.95em',
width: '3em',
height: '1em',
'&::before': {
borderWidth: '1em 1em 0 1em',
borderColor: `${color} transparent transparent transparent`,
},
},
'&[x-placement*="right"] $arrow': {
left: 0,
marginLeft: '-0.95em',
height: '3em',
width: '1em',
'&::before': {
borderWidth: '1em 1em 1em 0',
borderColor: `transparent ${color} transparent transparent`,
},
},
'&[x-placement*="left"] $arrow': {
right: 0,
marginRight: '-0.95em',
height: '3em',
width: '1em',
'&::before': {
borderWidth: '1em 0 1em 1em',
borderColor: `transparent transparent transparent ${color}`,
},
},
};
}

const useStylesArrow = makeStyles(theme => ({
tooltip: {
position: 'relative',
},
arrow: {
position: 'absolute',
fontSize: 6,
width: '3em',
height: '3em',
'&::before': {
content: '""',
margin: 'auto',
display: 'block',
width: 0,
height: 0,
borderStyle: 'solid',
},
},
popper: arrowGenerator(theme.palette.grey[700]),
}));

function ArrowTooltip(props) {
const {arrow, ...classes} = useStylesArrow();
const [arrowRef, setArrowRef] = React.useState(null);

return (
<Tooltip
classes={classes}
PopperProps={{
popperOptions: {
modifiers: {
arrow: {
enabled: Boolean(arrowRef),
element: arrowRef,
},
},
},
}}
{...props}
title={
<React.Fragment>
{props.title}
<span className={arrow} ref={setArrowRef} />
</React.Fragment>
}
/>
);
}

class Bx2PanelValidatorDetail extends Component {
makeMarker(node) {
return {
name: node.pubkey,
coordinates: [node.lng, node.lat],
};
}

renderValidators() {
const {nodes, id} = this.props;
const node = _.find(nodes, x => x.pubkey === id) || {};

return (
<div>
<Paper style={{marginTop: 60, marginLeft: 240}}>
<Typography
variant="h6"
id="tableTitle"
style={{textAlign: 'left', padding: '16px'}}
>
Validator Detail : {id}
<BxHelpLink text="Transaction" term="transaction" />
<br />
<Link component={RouterLink} to={'/v2/validators'}>
Return to list
</Link>
</Typography>
<Table>
<TableHead>
<TableRow>
<TableCell>
<div>
Node Pubkey
<BxHelpLink text="Transaction" term="transaction" />
</div>
<div>
Vote Pubkey
<BxHelpLink text="Account" term="account" />
</div>
</TableCell>
<TableCell>
Stake
<BxHelpLink text="Program" term="program-id" />
</TableCell>
<TableCell align="center">
Commission
<BxHelpLink text="Block Height" term="block-height" />
</TableCell>
<TableCell align="right">
Uptime
<BxHelpLink text="Block Height" term="block-height" />
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{_.map([node], row => (
<TableRow key={row.pubkey}>
<TableCell
component="th"
scope="row"
title={JSON.stringify(row, null, 2)}
>
<BxEntityLink prg_id={row.pubkey} />
<br />
<BxEntityLink
prg_id={row.voteAccount && row.voteAccount.votePubkey}
/>
</TableCell>
<TableCell align="right" style={{verticalAlign: 'middle'}}>
{(row.voteAccount && row.voteAccount.stake) || 0} Lamports
</TableCell>
<TableCell align="center">TODO</TableCell>
<TableCell align="right">TODO</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</Paper>
<p />
<div style={{backgroundColor: 'white', marginLeft: 240, marginTop: 60}}>
<ComposableMap>
<ZoomableGroup>
<Geographies geography={'/resources/world-50m-simplified.json'}>
{(geographies, projection) =>
geographies.map((geography, i) => (
<Geography
key={`geo${i}`}
geography={geography}
projection={projection}
/>
))
}
</Geographies>
<Markers>
{[node].map((node, i) => (
<Marker key={`mk${i}`} marker={this.makeMarker(node)}>
<ArrowTooltip
title={`Name: ${
node.pubkey
}\nStake: ${(node.voteAccount &&
node.voteAccount.stake) ||
0} Lamports`}
>
<circle
cx={0}
cy={0}
r={10}
style={{
stroke: 'rgb(21, 127, 94)',
fill: 'rgb(43, 254, 188)',
strokeWidth: 3,
opacity: 0.9,
}}
/>
</ArrowTooltip>
</Marker>
))}
</Markers>
</ZoomableGroup>
</ComposableMap>
</div>
</div>
);
}

render() {
const {nodes} = this.props;

if (nodes) {
return this.renderValidators();
}

return (
<Paper>
<Typography variant="h6" id="tableTitle" style={{textAlign: 'left'}}>
(ERROR - No data.)
</Typography>
</Paper>
);
}
}

Bx2PanelValidatorDetail.propTypes = {
nodes: PropTypes.array.isRequired,
id: PropTypes.string.isRequired,
};

export default Bx2PanelValidatorDetail;
2 changes: 1 addition & 1 deletion src/v2/Bx2PanelValidators.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class Bx2PanelValidators extends React.Component {
scope="row"
title={JSON.stringify(row, null, 2)}
>
<BxEntityLink prg_id={row.pubkey} />
<BxEntityLink validator_id={row.pubkey} />
<br />
<BxEntityLink
prg_id={row.voteAccount && row.voteAccount.votePubkey}
Expand Down
2 changes: 1 addition & 1 deletion src/v2/Bx2PanelValidatorsOverview.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ class Bx2PanelValidatorsOverview extends Component {
scope="row"
title={JSON.stringify(row, null, 2)}
>
<BxEntityLink prg_id={row.pubkey} />
<BxEntityLink validator_id={row.pubkey} />
<br />
<BxEntityLink
prg_id={row.voteAccount && row.voteAccount.votePubkey}
Expand Down

0 comments on commit fd7a9bc

Please sign in to comment.