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

Commit

Permalink
feat: validators search and details page update
Browse files Browse the repository at this point in the history
  • Loading branch information
manuel-calavera authored and sunnygleason committed Jul 26, 2019
1 parent 0b0f22c commit aab4795
Show file tree
Hide file tree
Showing 10 changed files with 233 additions and 55 deletions.
36 changes: 36 additions & 0 deletions src/v2/@types/validator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// @flow

export type Identity = {
avatarUrl: string,
details: string,
keybaseUsername: string,
name: string,
pubkey: string,
verified: string,
verifyUrl: string,
website: string,
};

export type UptimeItem = {
creditsEarned: number,
epoch: number,
percentage: string,
slotsInEpoch: number,
};

export type Uptime = {
lat: number,
ts: number,
votePubkey: string,
uptime: UptimeItem[],
};

export type Validator = {
commission: number,
coordinated: [number, number],
gossip: string,
identity: Identity,
nodePubkey: string,
stake: number,
uptime: Uptime,
};
20 changes: 9 additions & 11 deletions src/v2/components/Dashboard/NetworkOverview/StatCards/index.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// @flow

import React from 'react';
import {Link} from 'react-router-dom';
import {observer} from 'mobx-react-lite';
import {map} from 'lodash/fp';
import {Grid, Typography} from '@material-ui/core';
Expand All @@ -11,38 +12,35 @@ import NodesStore from 'v2/stores/nodes';
import useStyles from './styles';

const StatCards = () => {
const {globalStats, statsChanges} = OverviewStore;
const {cluster, clusterChanges} = NodesStore;
const {globalStats} = OverviewStore;
const {cluster} = NodesStore;
const classes = useStyles();

const cards = [
{
title: 'Node Count',
value: cluster.nodes.length,
changes: clusterChanges.nodes,
},
{
title: 'Block Height',
value: globalStats['!blkLastSlot'],
changes: statsChanges['!blkLastSlot'],
},
{
title: 'Transactions Count',
value: globalStats['!txnCount'],
changes: statsChanges['!txnCount'],
},
{
title: 'Current Leader',
value() {
return (
<Typography
noWrap
align="center"
variant="h2"
<Link
className={classes.leader}
to={`/rc/validators/${globalStats['!entLastLeader']}`}
>
{globalStats['!entLastLeader']}
</Typography>
<Typography noWrap align="center" variant="h2">
{globalStats['!entLastLeader']}
</Typography>
</Link>
);
},
},
Expand Down
13 changes: 8 additions & 5 deletions src/v2/components/Dashboard/NetworkOverview/StatCards/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@ export default makeStyles(theme => ({
margin: '20px 0',
},
leader: {
fontSize: 20,
fontWeight: 'bold',
color: getColor('main')(theme),
marginTop: 40,
letterSpacing: 3.4,
textDecoration: 'none',
'& h2': {
fontSize: 20,
fontWeight: 'bold',
color: getColor('main')(theme),
marginTop: 40,
letterSpacing: 3.4,
}
},
changes: {
fontSize: 18,
Expand Down
3 changes: 1 addition & 2 deletions src/v2/components/Header/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ const Header = () => {
const [isDrawerOpen, setDrawerOpen] = useState(false);
const {endpointName, updateEndpointName} = socketActions;
const classes = useStyles();
const onSearch = () => {};
const handleEndpointChange = (event: SyntheticEvent<HTMLSelectElement>) => {
const endpointName = event.currentTarget.value;
EndpointConfig.setEndpointName(endpointName);
Expand Down Expand Up @@ -50,7 +49,7 @@ const Header = () => {
<Toolbar classes={{root: classes.inner}}>
<Logo />
<div className={classes.search}>
<Search onSubmit={onSearch} />
<Search />
</div>
<div className={classes.realTime}>
<p>Real-time updated:</p>
Expand Down
85 changes: 62 additions & 23 deletions src/v2/components/Search/index.jsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,79 @@
// @flow
import React, {useState} from 'react';
import {observer} from 'mobx-react-lite';
import {compose, get, filter, lowerCase, contains} from 'lodash/fp';
import {InputBase, IconButton} from '@material-ui/core';
import {Search as SearchIcon} from '@material-ui/icons';
import NodesStore from 'v2/stores/nodes';

import SearchResult from './result';
import useStyles from './styles';

type SearchProps = {
onSubmit: (val: string) => void,
};

const Search = ({onSubmit}: SearchProps) => {
const Search = () => {
const classes = useStyles();
const {validators} = NodesStore;
const [value, setValue] = useState('');
const [isDirty, setDirty] = useState(false);
const [isFocus, setFocus] = useState(false);
const [searchResult, setSearchResult] = useState([]);

const onFocus = () => setFocus(true);
const onBlur = () => {
setTimeout(() => {
setFocus(false);
}, 250);
};

const handleClear = () => {
setDirty(false);
setValue('');
setSearchResult([]);
};

const handleSearch = ({currentTarget}: SyntheticEvent<HTMLInputElement>) => {
if (!currentTarget.value) {
handleClear();
return;
}
setValue(currentTarget.value);
const filteredValidators = filter(v => {
const lowerVal = lowerCase(value);
return (
compose(contains(lowerVal), lowerCase, get('nodePubkey'))(v) ||
compose(contains(lowerVal), lowerCase, get('identity.keybaseUsername'))(v)
);
})(validators);
setDirty(true);
setSearchResult(filteredValidators);
};
const handleSubmit = () => {
onSubmit(value);
};

return (
<form className={classes.root} onSubmit={handleSubmit}>
<InputBase
className={classes.input}
placeholder="Search by transaction hash / block number / application ID"
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
value={value}
onChange={handleSearch}
<div className={classes.root}>
<div className={classes.form}>
<InputBase
onFocus={onFocus}
onBlur={onBlur}
className={classes.input}
placeholder="Search by validators address / keybase"
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
value={value}
onChange={handleSearch}
/>
<IconButton size="small" className={classes.btn}>
<SearchIcon />
</IconButton>
</div>
<SearchResult
isDirty={isDirty}
isFocus={isFocus}
onClear={handleClear}
items={searchResult}
/>
<IconButton size="small" className={classes.btn}>
<SearchIcon />
</IconButton>
</form>
</div>
);
};

export default Search;
export default observer(Search);
41 changes: 41 additions & 0 deletions src/v2/components/Search/result.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// @flow
import React, {memo} from 'react';
import {map} from 'lodash/fp';
import {Link} from 'react-router-dom';
import type {Validator} from 'v2/@types/validator';

import useStyles from './styles';

type SearchResultProps = {
items: Validator[],
onClear: () => void,
isDirty: boolean,
isFocus: boolean,
};

const SearchResult = ({isDirty, isFocus, items, onClear}: SearchResultProps) => {
const classes = useStyles();
const renderItem = ({nodePubkey}: {nodePubkey: string}) => (
<li className={classes.item} key={nodePubkey}>
<Link onClick={onClear} to={`/rc/validators/${nodePubkey}`}>
{nodePubkey}
</Link>
</li>
);
if ((!isDirty && !items.length ) || !isFocus) {
return null;
}

return (
<div className={classes.list}>
<div className={classes.title}>
{!items.length
? 'There were no results for this search term.'
: 'Validators'}
</div>
<ul>{map(renderItem)(items)}</ul>
</div>
);
};

export default memo(SearchResult);
26 changes: 26 additions & 0 deletions src/v2/components/Search/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import getColor from 'v2/utils/getColor';
export default makeStyles(theme => {
return {
root: {
position: 'relative',
},
form: {
border: `1px solid ${getColor('grey')(theme)}`,
padding: 5,
display: 'flex',
Expand All @@ -20,5 +23,28 @@ export default makeStyles(theme => {
borderRadius: 0,
width: 40,
},
list: {
position: 'absolute',
background: getColor('white')(theme),
width: '100%',
padding: '12px 20px',
'& ul': {
padding: 0,
margin: 0,
'& a': {
color: getColor('grey3')(theme),
fontSize: 12,
textDecoration: 'none',
padding: '5px 0',
display: 'block',
'&:hover': {
color: getColor('dark')(theme)
}
}
},
},
title: {
color: getColor('dark')(theme)
}
};
});
Loading

0 comments on commit aab4795

Please sign in to comment.