diff options
Diffstat (limited to 'dashboard/assets/components')
-rw-r--r-- | dashboard/assets/components/CustomTooltip.jsx | 6 | ||||
-rw-r--r-- | dashboard/assets/components/Dashboard.jsx | 28 | ||||
-rw-r--r-- | dashboard/assets/components/Footer.jsx | 158 | ||||
-rw-r--r-- | dashboard/assets/components/Header.jsx | 50 | ||||
-rw-r--r-- | dashboard/assets/components/Main.jsx | 3 | ||||
-rw-r--r-- | dashboard/assets/components/SideBar.jsx | 4 |
6 files changed, 114 insertions, 135 deletions
diff --git a/dashboard/assets/components/CustomTooltip.jsx b/dashboard/assets/components/CustomTooltip.jsx index be7c624cf..3405f9305 100644 --- a/dashboard/assets/components/CustomTooltip.jsx +++ b/dashboard/assets/components/CustomTooltip.jsx @@ -38,15 +38,15 @@ export const percentPlotter = <T>(text: string, mapper: (T => T) = multiplier(1) }; // unit contains the units for the bytePlotter. -const unit = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']; +const unit = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi']; // simplifyBytes returns the simplified version of the given value followed by the unit. const simplifyBytes = (x: number) => { let i = 0; - for (; x > 1024 && i < 5; i++) { + for (; x > 1024 && i < 8; i++) { x /= 1024; } - return x.toFixed(2).toString().concat(' ', unit[i]); + return x.toFixed(2).toString().concat(' ', unit[i], 'B'); }; // bytePlotter renders a tooltip, which displays the payload as a byte value. diff --git a/dashboard/assets/components/Dashboard.jsx b/dashboard/assets/components/Dashboard.jsx index 90b1a785c..8e6bf9869 100644 --- a/dashboard/assets/components/Dashboard.jsx +++ b/dashboard/assets/components/Dashboard.jsx @@ -81,7 +81,11 @@ const defaultContent: Content = { version: null, commit: null, }, - home: { + home: {}, + chain: {}, + txpool: {}, + network: {}, + system: { activeMemory: [], virtualMemory: [], networkIngress: [], @@ -91,10 +95,6 @@ const defaultContent: Content = { diskRead: [], diskWrite: [], }, - chain: {}, - txpool: {}, - network: {}, - system: {}, logs: { log: [], }, @@ -108,7 +108,11 @@ const updaters = { version: replacer, commit: replacer, }, - home: { + home: null, + chain: null, + txpool: null, + network: null, + system: { activeMemory: appender(200), virtualMemory: appender(200), networkIngress: appender(200), @@ -118,11 +122,7 @@ const updaters = { diskRead: appender(200), diskWrite: appender(200), }, - chain: null, - txpool: null, - network: null, - system: null, - logs: { + logs: { log: appender(200), }, }; @@ -136,7 +136,7 @@ const styles = { height: '100%', zIndex: 1, overflow: 'hidden', - } + }, }; // themeStyles returns the styles generated from the theme for the component. @@ -178,7 +178,8 @@ class Dashboard extends Component<Props, State> { // reconnect establishes a websocket connection with the server, listens for incoming messages // and tries to reconnect on connection loss. reconnect = () => { - const server = new WebSocket(`${((window.location.protocol === 'https:') ? 'wss://' : 'ws://') + window.location.host}/api`); + // PROD is defined by webpack. + const server = new WebSocket(`${((window.location.protocol === 'https:') ? 'wss://' : 'ws://')}${PROD ? window.location.host : 'localhost:8080'}/api`); server.onopen = () => { this.setState({content: defaultContent, shouldUpdate: {}}); }; @@ -217,7 +218,6 @@ class Dashboard extends Component<Props, State> { return ( <div className={this.props.classes.dashboard} style={styles.dashboard}> <Header - opened={this.state.sideBar} switchSideBar={this.switchSideBar} /> <Body diff --git a/dashboard/assets/components/Footer.jsx b/dashboard/assets/components/Footer.jsx index 54b67c464..20830cbba 100644 --- a/dashboard/assets/components/Footer.jsx +++ b/dashboard/assets/components/Footer.jsx @@ -26,7 +26,17 @@ import {ResponsiveContainer, AreaChart, Area, Tooltip} from 'recharts'; import ChartRow from './ChartRow'; import CustomTooltip, {bytePlotter, bytePerSecPlotter, percentPlotter, multiplier} from './CustomTooltip'; import {styles as commonStyles} from '../common'; -import type {Content} from '../types/content'; +import type {General, System} from '../types/content'; + +const FOOTER_SYNC_ID = 'footerSyncId'; + +const CPU = 'cpu'; +const MEMORY = 'memory'; +const DISK = 'disk'; +const TRAFFIC = 'traffic'; + +const TOP = 'Top'; +const BOTTOM = 'Bottom'; // styles contains the constant styles of the component. const styles = { @@ -40,17 +50,16 @@ const styles = { padding: 0, }, doubleChartWrapper: { - height: '100%', - width: '99%', - paddingTop: 5, + height: '100%', + width: '99%', }, }; // themeStyles returns the styles generated from the theme for the component. const themeStyles: Object = (theme: Object) => ({ footer: { - backgroundColor: theme.palette.background.appBar, - color: theme.palette.getContrastText(theme.palette.background.appBar), + backgroundColor: theme.palette.grey[900], + color: theme.palette.getContrastText(theme.palette.grey[900]), zIndex: theme.zIndex.appBar, height: theme.spacing.unit * 10, }, @@ -59,111 +68,108 @@ const themeStyles: Object = (theme: Object) => ({ export type Props = { classes: Object, // injected by withStyles() theme: Object, - content: Content, + general: General, + system: System, shouldUpdate: Object, }; // Footer renders the footer of the dashboard. class Footer extends Component<Props> { shouldComponentUpdate(nextProps) { - return typeof nextProps.shouldUpdate.home !== 'undefined'; + return typeof nextProps.shouldUpdate.general !== 'undefined' || typeof nextProps.shouldUpdate.system !== 'undefined'; } - // info renders a label with the given values. - info = (about: string, value: ?string) => (value ? ( - <Typography type='caption' color='inherit'> - <span style={commonStyles.light}>{about}</span> {value} - </Typography> - ) : null); + // halfHeightChart renders an area chart with half of the height of its parent. + halfHeightChart = (chartProps, tooltip, areaProps) => ( + <ResponsiveContainer width='100%' height='50%'> + <AreaChart {...chartProps} > + {!tooltip || (<Tooltip cursor={false} content={<CustomTooltip tooltip={tooltip} />} />)} + <Area isAnimationActive={false} type='monotone' {...areaProps} /> + </AreaChart> + </ResponsiveContainer> + ); // doubleChart renders a pair of charts separated by the baseline. - doubleChart = (syncId, topChart, bottomChart) => { - const topKey = 'topKey'; - const bottomKey = 'bottomKey'; - const topDefault = topChart.default ? topChart.default : 0; - const bottomDefault = bottomChart.default ? bottomChart.default : 0; - const topTooltip = topChart.tooltip ? ( - <Tooltip cursor={false} content={<CustomTooltip tooltip={topChart.tooltip} />} /> - ) : null; - const bottomTooltip = bottomChart.tooltip ? ( - <Tooltip cursor={false} content={<CustomTooltip tooltip={bottomChart.tooltip} />} /> - ) : null; + doubleChart = (syncId, chartKey, topChart, bottomChart) => { + if (!Array.isArray(topChart.data) || !Array.isArray(bottomChart.data)) { + return null; + } + const topDefault = topChart.default || 0; + const bottomDefault = bottomChart.default || 0; + const topKey = `${chartKey}${TOP}`; + const bottomKey = `${chartKey}${BOTTOM}`; const topColor = '#8884d8'; const bottomColor = '#82ca9d'; - // Put the samples of the two charts into the same array in order to avoid problems - // at the synchronized area charts. If one of the two arrays doesn't have value at - // a given position, give it a 0 default value. - let data = [...topChart.data.map(({value}) => { - const d = {}; - d[topKey] = value || topDefault; - return d; - })]; - for (let i = 0; i < data.length && i < bottomChart.data.length; i++) { - // The value needs to be negative in order to plot it upside down. - const d = bottomChart.data[i]; - data[i][bottomKey] = d && d.value ? -d.value : bottomDefault; - } - data = [...data, ...bottomChart.data.slice(data.length).map(({value}) => { - const d = {}; - d[topKey] = topDefault; - d[bottomKey] = -value || bottomDefault; - return d; - })]; - return ( <div style={styles.doubleChartWrapper}> - <ResponsiveContainer width='100%' height='50%'> - <AreaChart data={data} syncId={syncId} > - {topTooltip} - <Area type='monotone' dataKey={topKey} stroke={topColor} fill={topColor} /> - </AreaChart> - </ResponsiveContainer> - <div style={{marginTop: -10, width: '100%', height: '50%'}}> - <ResponsiveContainer width='100%' height='100%'> - <AreaChart data={data} syncId={syncId} > - {bottomTooltip} - <Area type='monotone' dataKey={bottomKey} stroke={bottomColor} fill={bottomColor} /> - </AreaChart> - </ResponsiveContainer> - </div> + {this.halfHeightChart( + { + syncId, + data: topChart.data.map(({value}) => ({[topKey]: value || topDefault})), + margin: {top: 5, right: 5, bottom: 0, left: 5}, + }, + topChart.tooltip, + {dataKey: topKey, stroke: topColor, fill: topColor}, + )} + {this.halfHeightChart( + { + syncId, + data: bottomChart.data.map(({value}) => ({[bottomKey]: -value || -bottomDefault})), + margin: {top: 0, right: 5, bottom: 5, left: 5}, + }, + bottomChart.tooltip, + {dataKey: bottomKey, stroke: bottomColor, fill: bottomColor}, + )} </div> ); - } + }; render() { - const {content} = this.props; - const {general, home} = content; + const {general, system} = this.props; return ( <Grid container className={this.props.classes.footer} direction='row' alignItems='center' style={styles.footer}> <Grid item xs style={styles.chartRowWrapper}> <ChartRow> {this.doubleChart( - 'all', - {data: home.processCPU, tooltip: percentPlotter('Process')}, - {data: home.systemCPU, tooltip: percentPlotter('System', multiplier(-1))}, + FOOTER_SYNC_ID, + CPU, + {data: system.processCPU, tooltip: percentPlotter('Process load')}, + {data: system.systemCPU, tooltip: percentPlotter('System load', multiplier(-1))}, )} {this.doubleChart( - 'all', - {data: home.activeMemory, tooltip: bytePlotter('Active')}, - {data: home.virtualMemory, tooltip: bytePlotter('Virtual', multiplier(-1))}, + FOOTER_SYNC_ID, + MEMORY, + {data: system.activeMemory, tooltip: bytePlotter('Active memory')}, + {data: system.virtualMemory, tooltip: bytePlotter('Virtual memory', multiplier(-1))}, )} {this.doubleChart( - 'all', - {data: home.diskRead, tooltip: bytePerSecPlotter('Disk Read')}, - {data: home.diskWrite, tooltip: bytePerSecPlotter('Disk Write', multiplier(-1))}, + FOOTER_SYNC_ID, + DISK, + {data: system.diskRead, tooltip: bytePerSecPlotter('Disk read')}, + {data: system.diskWrite, tooltip: bytePerSecPlotter('Disk write', multiplier(-1))}, )} {this.doubleChart( - 'all', - {data: home.networkIngress, tooltip: bytePerSecPlotter('Download')}, - {data: home.networkEgress, tooltip: bytePerSecPlotter('Upload', multiplier(-1))}, + FOOTER_SYNC_ID, + TRAFFIC, + {data: system.networkIngress, tooltip: bytePerSecPlotter('Download')}, + {data: system.networkEgress, tooltip: bytePerSecPlotter('Upload', multiplier(-1))}, )} </ChartRow> </Grid> <Grid item > - {this.info('Geth', general.version)} - {this.info('Commit', general.commit ? general.commit.substring(0, 7) : null)} + <Typography type='caption' color='inherit'> + <span style={commonStyles.light}>Geth</span> {general.version} + </Typography> + {general.commit && ( + <Typography type='caption' color='inherit'> + <span style={commonStyles.light}>{'Commit '}</span> + <a href={`https://github.com/ethereum/go-ethereum/commit/${general.commit}`} target='_blank' style={{color: 'inherit', textDecoration: 'none'}} > + {general.commit.substring(0, 8)} + </a> + </Typography> + )} </Grid> </Grid> ); diff --git a/dashboard/assets/components/Header.jsx b/dashboard/assets/components/Header.jsx index e91885af3..ccdfbc6f0 100644 --- a/dashboard/assets/components/Header.jsx +++ b/dashboard/assets/components/Header.jsx @@ -21,30 +21,16 @@ import React, {Component} from 'react'; import withStyles from 'material-ui/styles/withStyles'; import AppBar from 'material-ui/AppBar'; import Toolbar from 'material-ui/Toolbar'; -import Transition from 'react-transition-group/Transition'; import IconButton from 'material-ui/IconButton'; +import Icon from 'material-ui/Icon'; +import MenuIcon from 'material-ui-icons/Menu'; import Typography from 'material-ui/Typography'; -import ChevronLeftIcon from 'material-ui-icons/ChevronLeft'; - -import {DURATION} from '../common'; - -// styles contains the constant styles of the component. -const styles = { - arrow: { - default: { - transition: `transform ${DURATION}ms`, - }, - transition: { - entered: {transform: 'rotate(180deg)'}, - }, - }, -}; // themeStyles returns the styles generated from the theme for the component. const themeStyles = (theme: Object) => ({ header: { - backgroundColor: theme.palette.background.appBar, - color: theme.palette.getContrastText(theme.palette.background.appBar), + backgroundColor: theme.palette.grey[900], + color: theme.palette.getContrastText(theme.palette.grey[900]), zIndex: theme.zIndex.appBar, }, toolbar: { @@ -53,42 +39,28 @@ const themeStyles = (theme: Object) => ({ }, title: { paddingLeft: theme.spacing.unit, + fontSize: 3 * theme.spacing.unit, }, }); export type Props = { classes: Object, // injected by withStyles() - opened: boolean, switchSideBar: () => void, }; // Header renders the header of the dashboard. class Header extends Component<Props> { - shouldComponentUpdate(nextProps) { - return nextProps.opened !== this.props.opened; - } - - // arrow renders a button, which changes the sidebar's state. - arrow = (transitionState: string) => ( - <IconButton onClick={this.props.switchSideBar}> - <ChevronLeftIcon - style={{ - ...styles.arrow.default, - ...styles.arrow.transition[transitionState], - }} - /> - </IconButton> - ); - render() { - const {classes, opened} = this.props; + const {classes} = this.props; return ( <AppBar position='static' className={classes.header}> <Toolbar className={classes.toolbar}> - <Transition mountOnEnter in={opened} timeout={{enter: DURATION}}> - {this.arrow} - </Transition> + <IconButton onClick={this.props.switchSideBar}> + <Icon> + <MenuIcon /> + </Icon> + </IconButton> <Typography type='title' color='inherit' noWrap className={classes.title}> Go Ethereum Dashboard </Typography> diff --git a/dashboard/assets/components/Main.jsx b/dashboard/assets/components/Main.jsx index a9e3d3578..fba8ca1f6 100644 --- a/dashboard/assets/components/Main.jsx +++ b/dashboard/assets/components/Main.jsx @@ -76,7 +76,8 @@ class Main extends Component<Props> { <div style={styles.wrapper}> <div className={classes.content} style={styles.content}>{children}</div> <Footer - content={content} + general={content.general} + system={content.system} shouldUpdate={shouldUpdate} /> </div> diff --git a/dashboard/assets/components/SideBar.jsx b/dashboard/assets/components/SideBar.jsx index c2e419ae9..463d6cb40 100644 --- a/dashboard/assets/components/SideBar.jsx +++ b/dashboard/assets/components/SideBar.jsx @@ -41,10 +41,10 @@ const styles = { // themeStyles returns the styles generated from the theme for the component. const themeStyles = theme => ({ list: { - background: theme.palette.background.appBar, + background: theme.palette.grey[900], }, listItem: { - minWidth: theme.spacing.unit * 3, + minWidth: theme.spacing.unit * 7, }, icon: { fontSize: theme.spacing.unit * 3, |