mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-17 09:23:48 +00:00
Merge ff5987e597 into 50df2b78be
This commit is contained in:
commit
494f2e2823
17 changed files with 38813 additions and 344 deletions
|
|
@ -22,10 +22,9 @@ $ (cd dashboard/assets && ./node_modules/.bin/webpack --watch)
|
||||||
$ geth --dashboard --dashboard.assets=dashboard/assets/public --vmodule=dashboard=5
|
$ geth --dashboard --dashboard.assets=dashboard/assets/public --vmodule=dashboard=5
|
||||||
```
|
```
|
||||||
|
|
||||||
To bundle up the final UI into Geth, run `webpack` and `go generate`:
|
To bundle up the final UI into Geth, run `go generate`:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ (cd dashboard/assets && ./node_modules/.bin/webpack)
|
|
||||||
$ go generate ./dashboard
|
$ go generate ./dashboard
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
38402
dashboard/assets.go
38402
dashboard/assets.go
File diff suppressed because one or more lines are too long
68
dashboard/assets/components/Body.jsx
Normal file
68
dashboard/assets/components/Body.jsx
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
// Copyright 2017 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import withStyles from 'material-ui/styles/withStyles';
|
||||||
|
|
||||||
|
import SideBar from './SideBar.jsx';
|
||||||
|
import Content from "./Content.jsx";
|
||||||
|
|
||||||
|
// Styles for the Body component.
|
||||||
|
const styles = theme => ({
|
||||||
|
body: {
|
||||||
|
display: 'flex',
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Body renders the body of the dashboard.
|
||||||
|
@withStyles(styles)
|
||||||
|
class Body extends Component {
|
||||||
|
render() {
|
||||||
|
const {classes} = this.props; // The classes property is injected by withStyles().
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes.body}>
|
||||||
|
<SideBar
|
||||||
|
opened={this.props.opened}
|
||||||
|
changeContent={this.props.changeContent}
|
||||||
|
/>
|
||||||
|
<Content
|
||||||
|
active={this.props.active}
|
||||||
|
memory={this.props.memory}
|
||||||
|
traffic={this.props.traffic}
|
||||||
|
logs={this.props.logs}
|
||||||
|
shouldUpdate={this.props.shouldUpdate}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Body.propTypes = {
|
||||||
|
opened: PropTypes.bool.isRequired,
|
||||||
|
changeContent: PropTypes.func.isRequired,
|
||||||
|
active: PropTypes.string.isRequired,
|
||||||
|
memory: PropTypes.array.isRequired,
|
||||||
|
traffic: PropTypes.array.isRequired,
|
||||||
|
logs: PropTypes.array.isRequired,
|
||||||
|
shouldUpdate: PropTypes.object.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Body;
|
||||||
48
dashboard/assets/components/ChartGrid.jsx
Normal file
48
dashboard/assets/components/ChartGrid.jsx
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
// Copyright 2017 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import Grid from 'material-ui/Grid';
|
||||||
|
import {ResponsiveContainer} from 'recharts';
|
||||||
|
|
||||||
|
|
||||||
|
// ChartGrid renders a grid container for responsive charts.
|
||||||
|
// The children are Recharts components extended with the Material-UI's xs property.
|
||||||
|
class ChartGrid extends Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Grid container spacing={this.props.spacing}>
|
||||||
|
{
|
||||||
|
React.Children.map(this.props.children, child => (
|
||||||
|
<Grid item xs={child.props.xs}>
|
||||||
|
<ResponsiveContainer width="100%" height={child.props.height}>
|
||||||
|
{React.cloneElement(child, {data: child.props.values.map(value => ({value: value}))})}
|
||||||
|
</ResponsiveContainer>
|
||||||
|
</Grid>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ChartGrid.propTypes = {
|
||||||
|
spacing: PropTypes.number.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ChartGrid;
|
||||||
|
|
@ -22,15 +22,16 @@ export const LIMIT = {
|
||||||
traffic: 200, // Maximum number of traffic data samples.
|
traffic: 200, // Maximum number of traffic data samples.
|
||||||
log: 200, // Maximum number of logs.
|
log: 200, // Maximum number of logs.
|
||||||
};
|
};
|
||||||
|
|
||||||
// The sidebar menu and the main content are rendered based on these elements.
|
// The sidebar menu and the main content are rendered based on these elements.
|
||||||
export const TAGS = (() => {
|
export const TAGS = (() => {
|
||||||
const T = {
|
const T = {
|
||||||
home: { title: "Home", },
|
home: { title: "Home", icon: "home", },
|
||||||
chain: { title: "Chain", },
|
chain: { title: "Chain", icon: "link", },
|
||||||
transactions: { title: "Transactions", },
|
transactions: { title: "Transactions", icon: "credit-card", },
|
||||||
network: { title: "Network", },
|
network: { title: "Network", icon: "globe", },
|
||||||
system: { title: "System", },
|
system: { title: "System", icon: "tachometer", },
|
||||||
logs: { title: "Logs", },
|
logs: { title: "Logs", icon: "list", },
|
||||||
};
|
};
|
||||||
// Using the key is circumstantial in some cases, so it is better to insert it also as a value.
|
// Using the key is circumstantial in some cases, so it is better to insert it also as a value.
|
||||||
// This way the mistyping is prevented.
|
// This way the mistyping is prevented.
|
||||||
|
|
@ -48,5 +49,4 @@ export const DATA_KEYS = (() => {
|
||||||
return DK;
|
return DK;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// Temporary - taken from Material-UI
|
export const DURATION = 200;
|
||||||
export const DRAWER_WIDTH = 240;
|
|
||||||
|
|
|
||||||
71
dashboard/assets/components/Content.jsx
Normal file
71
dashboard/assets/components/Content.jsx
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
// Copyright 2017 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import withStyles from 'material-ui/styles/withStyles';
|
||||||
|
|
||||||
|
import Home from './Home.jsx';
|
||||||
|
import {TAGS} from './Common.jsx';
|
||||||
|
|
||||||
|
// Styles for the Content component.
|
||||||
|
const styles = theme => ({
|
||||||
|
content: {
|
||||||
|
flexGrow: 1,
|
||||||
|
backgroundColor: theme.palette.background.default,
|
||||||
|
padding: theme.spacing.unit * 3,
|
||||||
|
overflow: 'auto',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Content renders the chosen content.
|
||||||
|
@withStyles(styles)
|
||||||
|
class Content extends Component {
|
||||||
|
render() {
|
||||||
|
const {classes, active, memory, traffic, logs, shouldUpdate} = this.props;
|
||||||
|
|
||||||
|
let content = null;
|
||||||
|
switch(active) {
|
||||||
|
case TAGS.home.id:
|
||||||
|
content = <Home memory={memory} traffic={traffic} shouldUpdate={shouldUpdate} />;
|
||||||
|
break;
|
||||||
|
case TAGS.chain.id:
|
||||||
|
content = <div>Chain is under construction.</div>;
|
||||||
|
break;
|
||||||
|
case TAGS.transactions.id:
|
||||||
|
content = <div>Transactions is under construction.</div>;
|
||||||
|
break;
|
||||||
|
case TAGS.network.id:
|
||||||
|
content = <div>Network is under construction.</div>;
|
||||||
|
break;
|
||||||
|
case TAGS.system.id:
|
||||||
|
content = <div>System is under construction.</div>;
|
||||||
|
break;
|
||||||
|
case TAGS.logs.id:
|
||||||
|
content = <div>{logs.map((log, index) => <div key={index}>{log}</div>)}</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div className={classes.content}>{content}</div>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Content.propTypes = {
|
||||||
|
active: PropTypes.string.isRequired,
|
||||||
|
shouldUpdate: PropTypes.object.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Content;
|
||||||
|
|
@ -15,27 +15,29 @@
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import {withStyles} from 'material-ui/styles';
|
|
||||||
|
|
||||||
import SideBar from './SideBar.jsx';
|
import withStyles from 'material-ui/styles/withStyles';
|
||||||
|
|
||||||
import Header from './Header.jsx';
|
import Header from './Header.jsx';
|
||||||
import Main from "./Main.jsx";
|
import Body from './Body.jsx';
|
||||||
import {isNullOrUndefined, LIMIT, TAGS, DATA_KEYS,} from "./Common.jsx";
|
import {isNullOrUndefined, LIMIT, TAGS, DATA_KEYS} from "./Common.jsx";
|
||||||
|
|
||||||
// Styles for the Dashboard component.
|
// Styles for the Dashboard component.
|
||||||
const styles = theme => ({
|
const styles = theme => ({
|
||||||
appFrame: {
|
dashboard: {
|
||||||
position: 'relative',
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
flexFlow: 'column',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
background: theme.palette.background.default,
|
background: theme.palette.background.default,
|
||||||
|
zIndex: 1,
|
||||||
|
overflow: 'hidden',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Dashboard is the main component, which renders the whole page, makes connection with the server and listens for messages.
|
// Dashboard is the main component, which renders the whole page, makes connection with the server and listens for messages.
|
||||||
// When there is an incoming message, updates the page's content correspondingly.
|
// When there is an incoming message, updates the page's content correspondingly.
|
||||||
|
@withStyles(styles)
|
||||||
class Dashboard extends Component {
|
class Dashboard extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
@ -45,7 +47,7 @@ class Dashboard extends Component {
|
||||||
memory: [],
|
memory: [],
|
||||||
traffic: [],
|
traffic: [],
|
||||||
logs: [],
|
logs: [],
|
||||||
shouldUpdate: {},
|
shouldUpdate: {}, // contains the labels of the incoming sample types
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,7 +76,6 @@ class Dashboard extends Component {
|
||||||
|
|
||||||
// update analyzes the incoming message, and updates the charts' content correspondingly.
|
// update analyzes the incoming message, and updates the charts' content correspondingly.
|
||||||
update = msg => {
|
update = msg => {
|
||||||
console.log(msg);
|
|
||||||
this.setState(prevState => {
|
this.setState(prevState => {
|
||||||
let newState = [];
|
let newState = [];
|
||||||
newState.shouldUpdate = {};
|
newState.shouldUpdate = {};
|
||||||
|
|
@ -121,9 +122,9 @@ class Dashboard extends Component {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// The change of the active label on the SideBar component will trigger a new render in the Main component.
|
// changeContent sets the active label, which is used at the content rendering.
|
||||||
changeContent = active => {
|
changeContent = newActive => {
|
||||||
this.setState(prevState => prevState.active !== active ? {active: active} : {});
|
this.setState(prevState => prevState.active !== newActive ? {active: newActive} : {});
|
||||||
};
|
};
|
||||||
|
|
||||||
openSideBar = () => {
|
openSideBar = () => {
|
||||||
|
|
@ -135,22 +136,18 @@ class Dashboard extends Component {
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
// The classes property is injected by withStyles().
|
const {classes} = this.props; // The classes property is injected by withStyles().
|
||||||
const {classes} = this.props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.appFrame}>
|
<div className={classes.dashboard}>
|
||||||
<Header
|
<Header
|
||||||
opened={this.state.sideBar}
|
opened={this.state.sideBar}
|
||||||
open={this.openSideBar}
|
openSideBar={this.openSideBar}
|
||||||
|
closeSideBar={this.closeSideBar}
|
||||||
/>
|
/>
|
||||||
<SideBar
|
<Body
|
||||||
opened={this.state.sideBar}
|
opened={this.state.sideBar}
|
||||||
close={this.closeSideBar}
|
|
||||||
changeContent={this.changeContent}
|
changeContent={this.changeContent}
|
||||||
/>
|
|
||||||
<Main
|
|
||||||
opened={this.state.sideBar}
|
|
||||||
active={this.state.active}
|
active={this.state.active}
|
||||||
memory={this.state.memory}
|
memory={this.state.memory}
|
||||||
traffic={this.state.traffic}
|
traffic={this.state.traffic}
|
||||||
|
|
@ -162,8 +159,4 @@ class Dashboard extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Dashboard.propTypes = {
|
export default Dashboard;
|
||||||
classes: PropTypes.object.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default withStyles(styles)(Dashboard);
|
|
||||||
|
|
|
||||||
|
|
@ -16,61 +16,76 @@
|
||||||
|
|
||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
|
||||||
import {withStyles} from 'material-ui/styles';
|
import withStyles from 'material-ui/styles/withStyles';
|
||||||
import AppBar from 'material-ui/AppBar';
|
import AppBar from 'material-ui/AppBar';
|
||||||
import Toolbar from 'material-ui/Toolbar';
|
import Toolbar from "material-ui/Toolbar";
|
||||||
|
import Transition from 'react-transition-group/Transition';
|
||||||
|
import IconButton from "material-ui/IconButton";
|
||||||
import Typography from 'material-ui/Typography';
|
import Typography from 'material-ui/Typography';
|
||||||
import IconButton from 'material-ui/IconButton';
|
import ChevronLeftIcon from 'material-ui-icons/ChevronLeft';
|
||||||
import MenuIcon from 'material-ui-icons/Menu';
|
|
||||||
|
|
||||||
import {DRAWER_WIDTH} from './Common.jsx';
|
import {DURATION} from './Common.jsx';
|
||||||
|
|
||||||
|
// arrowDefault is the default style of the arrow button.
|
||||||
|
const arrowDefault = {
|
||||||
|
transition: `transform ${DURATION}ms`,
|
||||||
|
};
|
||||||
|
// arrowTransition is the additional style of the arrow button corresponding to the transition's state.
|
||||||
|
const arrowTransition = {
|
||||||
|
entered: { transform: "rotate(180deg)" },
|
||||||
|
};
|
||||||
// Styles for the Header component.
|
// Styles for the Header component.
|
||||||
const styles = theme => ({
|
const styles = theme => ({
|
||||||
appBar: {
|
header: {
|
||||||
position: 'absolute',
|
backgroundColor: theme.palette.background.appBar,
|
||||||
transition: theme.transitions.create(['margin', 'width'], {
|
color: theme.palette.getContrastText(theme.palette.background.appBar),
|
||||||
easing: theme.transitions.easing.sharp,
|
zIndex: theme.zIndex.appBar,
|
||||||
duration: theme.transitions.duration.leavingScreen,
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
appBarShift: {
|
toolbar: {
|
||||||
marginLeft: DRAWER_WIDTH,
|
paddingLeft: theme.spacing.unit,
|
||||||
width: `calc(100% - ${DRAWER_WIDTH}px)`,
|
paddingRight: theme.spacing.unit,
|
||||||
transition: theme.transitions.create(['margin', 'width'], {
|
|
||||||
easing: theme.transitions.easing.easeOut,
|
|
||||||
duration: theme.transitions.duration.enteringScreen,
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
menuButton: {
|
mainText: {
|
||||||
marginLeft: 12,
|
paddingLeft: theme.spacing.unit,
|
||||||
marginRight: 20,
|
|
||||||
},
|
|
||||||
hide: {
|
|
||||||
display: 'none',
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Header renders a header, which contains a sidebar opener icon when that is closed.
|
// Header renders the header of the dashboard.
|
||||||
|
@withStyles(styles)
|
||||||
class Header extends Component {
|
class Header extends Component {
|
||||||
|
shouldComponentUpdate(nextProps) {
|
||||||
|
return nextProps.opened !== this.props.opened;
|
||||||
|
}
|
||||||
|
|
||||||
|
// changeSideBar opens or closes the sidebar corresponding to the previous state.
|
||||||
|
changeSideBar = () => {
|
||||||
|
this.props.opened ? this.props.closeSideBar() : this.props.openSideBar();
|
||||||
|
};
|
||||||
|
|
||||||
|
// arrowButton is connected to the sidebar; changes its state.
|
||||||
|
arrowButton = transitionState => (
|
||||||
|
<IconButton onClick={this.changeSideBar}>
|
||||||
|
<ChevronLeftIcon
|
||||||
|
style={{
|
||||||
|
...arrowDefault,
|
||||||
|
...arrowTransition[transitionState],
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</IconButton>
|
||||||
|
);
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
// The classes property is injected by withStyles().
|
const {classes, opened} = this.props; // The classes property is injected by withStyles().
|
||||||
const {classes} = this.props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppBar className={classNames(classes.appBar, this.props.opened && classes.appBarShift)}>
|
<AppBar position="static" className={classes.header}>
|
||||||
<Toolbar disableGutters={!this.props.opened}>
|
<Toolbar className={classes.toolbar}>
|
||||||
<IconButton
|
<Transition mountOnEnter in={opened} timeout={{enter: DURATION}}>
|
||||||
color="contrast"
|
{this.arrowButton}
|
||||||
aria-label="open drawer"
|
</Transition>
|
||||||
onClick={this.props.open}
|
<Typography type="title" color="inherit" noWrap className={classes.mainText}>
|
||||||
className={classNames(classes.menuButton, this.props.opened && classes.hide)}
|
PoC Go Ethereum Dashboard
|
||||||
>
|
|
||||||
<MenuIcon />
|
|
||||||
</IconButton>
|
|
||||||
<Typography type="title" color="inherit" noWrap>
|
|
||||||
Go Ethereum Dashboard
|
|
||||||
</Typography>
|
</Typography>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
|
|
@ -79,9 +94,9 @@ class Header extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
Header.propTypes = {
|
Header.propTypes = {
|
||||||
classes: PropTypes.object.isRequired,
|
|
||||||
opened: PropTypes.bool.isRequired,
|
opened: PropTypes.bool.isRequired,
|
||||||
open: PropTypes.func.isRequired,
|
openSideBar: PropTypes.func.isRequired,
|
||||||
|
closeSideBar: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withStyles(styles)(Header);
|
export default Header;
|
||||||
|
|
|
||||||
|
|
@ -16,65 +16,48 @@
|
||||||
|
|
||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import Grid from 'material-ui/Grid';
|
|
||||||
import {LineChart, AreaChart, Area, YAxis, CartesianGrid, Line, ResponsiveContainer} from 'recharts';
|
|
||||||
import {withTheme} from 'material-ui/styles';
|
|
||||||
|
|
||||||
|
import withTheme from 'material-ui/styles/withTheme';
|
||||||
|
import {LineChart, AreaChart, Area, YAxis, CartesianGrid, Line} from 'recharts';
|
||||||
|
|
||||||
|
import ChartGrid from './ChartGrid.jsx';
|
||||||
import {isNullOrUndefined, DATA_KEYS} from "./Common.jsx";
|
import {isNullOrUndefined, DATA_KEYS} from "./Common.jsx";
|
||||||
|
|
||||||
// ChartGrid renders a grid container for responsive charts.
|
// Home renders the home content.
|
||||||
// The children are Recharts components extended with the Material-UI's xs property.
|
@withTheme()
|
||||||
class ChartGrid extends Component {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<Grid container spacing={this.props.spacing}>
|
|
||||||
{
|
|
||||||
React.Children.map(this.props.children, child => (
|
|
||||||
<Grid item xs={child.props.xs}>
|
|
||||||
<ResponsiveContainer width="100%" height={child.props.height}>
|
|
||||||
{React.cloneElement(child, {data: child.props.values.map(value => ({value: value}))})}
|
|
||||||
</ResponsiveContainer>
|
|
||||||
</Grid>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</Grid>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ChartGrid.propTypes = {
|
|
||||||
spacing: PropTypes.number.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Home renders the home component.
|
|
||||||
class Home extends Component {
|
class Home extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
const {theme} = props; // The theme property is injected by withTheme().
|
||||||
|
this.memoryColor = theme.palette.primary[300];
|
||||||
|
this.trafficColor = theme.palette.secondary[300];
|
||||||
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps) {
|
shouldComponentUpdate(nextProps) {
|
||||||
return !isNullOrUndefined(nextProps.shouldUpdate[DATA_KEYS.memory]) ||
|
return !isNullOrUndefined(nextProps.shouldUpdate[DATA_KEYS.memory]) ||
|
||||||
!isNullOrUndefined(nextProps.shouldUpdate[DATA_KEYS.traffic]);
|
!isNullOrUndefined(nextProps.shouldUpdate[DATA_KEYS.traffic]);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {theme} = this.props;
|
const {memory, traffic} = this.props;
|
||||||
const memoryColor = theme.palette.primary[300];
|
|
||||||
const trafficColor = theme.palette.secondary[300];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ChartGrid spacing={24}>
|
<ChartGrid spacing={24}>
|
||||||
<AreaChart xs={6} height={300} values={this.props.memory}>
|
<AreaChart xs={6} height={300} values={memory}>
|
||||||
<YAxis />
|
<YAxis />
|
||||||
<Area type="monotone" dataKey="value" stroke={memoryColor} fill={memoryColor} />
|
<Area type="monotone" dataKey="value" stroke={this.memoryColor} fill={this.memoryColor} />
|
||||||
</AreaChart>
|
</AreaChart>
|
||||||
<LineChart xs={6} height={300} values={this.props.traffic}>
|
<LineChart xs={6} height={300} values={traffic}>
|
||||||
<Line type="monotone" dataKey="value" stroke={trafficColor} dot={false} />
|
<Line type="monotone" dataKey="value" stroke={this.trafficColor} dot={false} />
|
||||||
</LineChart>
|
</LineChart>
|
||||||
<LineChart xs={6} height={300} values={this.props.memory}>
|
<LineChart xs={6} height={300} values={memory}>
|
||||||
<YAxis />
|
<YAxis />
|
||||||
<CartesianGrid stroke="#eee" strokeDasharray="5 5" />
|
<CartesianGrid stroke="#eee" strokeDasharray="5 5" />
|
||||||
<Line type="monotone" dataKey="value" stroke={memoryColor} dot={false} />
|
<Line type="monotone" dataKey="value" stroke={this.memoryColor} dot={false} />
|
||||||
</LineChart>
|
</LineChart>
|
||||||
<AreaChart xs={6} height={300} values={this.props.traffic}>
|
<AreaChart xs={6} height={300} values={traffic}>
|
||||||
<CartesianGrid stroke="#eee" strokeDasharray="5 5" vertical={false} />
|
<CartesianGrid stroke="#eee" strokeDasharray="5 5" vertical={false} />
|
||||||
<Area type="monotone" dataKey="value" stroke={trafficColor} fill={trafficColor} />
|
<Area type="monotone" dataKey="value" stroke={this.trafficColor} fill={this.trafficColor} />
|
||||||
</AreaChart>
|
</AreaChart>
|
||||||
</ChartGrid>
|
</ChartGrid>
|
||||||
);
|
);
|
||||||
|
|
@ -82,8 +65,7 @@ class Home extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
Home.propTypes = {
|
Home.propTypes = {
|
||||||
theme: PropTypes.object.isRequired,
|
|
||||||
shouldUpdate: PropTypes.object.isRequired,
|
shouldUpdate: PropTypes.object.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withTheme()(Home);
|
export default Home;
|
||||||
|
|
|
||||||
|
|
@ -1,109 +0,0 @@
|
||||||
// Copyright 2017 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
import React, {Component} from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import {withStyles} from 'material-ui/styles';
|
|
||||||
|
|
||||||
import {TAGS, DRAWER_WIDTH} from "./Common.jsx";
|
|
||||||
import Home from './Home.jsx';
|
|
||||||
|
|
||||||
// ContentSwitch chooses and renders the proper page content.
|
|
||||||
class ContentSwitch extends Component {
|
|
||||||
render() {
|
|
||||||
switch(this.props.active) {
|
|
||||||
case TAGS.home.id:
|
|
||||||
return <Home memory={this.props.memory} traffic={this.props.traffic} shouldUpdate={this.props.shouldUpdate} />;
|
|
||||||
case TAGS.chain.id:
|
|
||||||
return null;
|
|
||||||
case TAGS.transactions.id:
|
|
||||||
return null;
|
|
||||||
case TAGS.network.id:
|
|
||||||
// Only for testing.
|
|
||||||
return null;
|
|
||||||
case TAGS.system.id:
|
|
||||||
return null;
|
|
||||||
case TAGS.logs.id:
|
|
||||||
return <div>{this.props.logs.map((log, index) => <div key={index}>{log}</div>)}</div>;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSwitch.propTypes = {
|
|
||||||
active: PropTypes.string.isRequired,
|
|
||||||
shouldUpdate: PropTypes.object.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
// styles contains the styles for the Main component.
|
|
||||||
const styles = theme => ({
|
|
||||||
content: {
|
|
||||||
width: '100%',
|
|
||||||
marginLeft: -DRAWER_WIDTH,
|
|
||||||
flexGrow: 1,
|
|
||||||
backgroundColor: theme.palette.background.default,
|
|
||||||
padding: theme.spacing.unit * 3,
|
|
||||||
transition: theme.transitions.create('margin', {
|
|
||||||
easing: theme.transitions.easing.sharp,
|
|
||||||
duration: theme.transitions.duration.leavingScreen,
|
|
||||||
}),
|
|
||||||
marginTop: 56,
|
|
||||||
overflow: 'auto',
|
|
||||||
[theme.breakpoints.up('sm')]: {
|
|
||||||
content: {
|
|
||||||
height: 'calc(100% - 64px)',
|
|
||||||
marginTop: 64,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
contentShift: {
|
|
||||||
marginLeft: 0,
|
|
||||||
transition: theme.transitions.create('margin', {
|
|
||||||
easing: theme.transitions.easing.easeOut,
|
|
||||||
duration: theme.transitions.duration.enteringScreen,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Main renders a component for the page content.
|
|
||||||
class Main extends Component {
|
|
||||||
render() {
|
|
||||||
// The classes property is injected by withStyles().
|
|
||||||
const {classes} = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<main className={classNames(classes.content, this.props.opened && classes.contentShift)}>
|
|
||||||
<ContentSwitch
|
|
||||||
active={this.props.active}
|
|
||||||
memory={this.props.memory}
|
|
||||||
traffic={this.props.traffic}
|
|
||||||
logs={this.props.logs}
|
|
||||||
shouldUpdate={this.props.shouldUpdate}
|
|
||||||
/>
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Main.propTypes = {
|
|
||||||
classes: PropTypes.object.isRequired,
|
|
||||||
opened: PropTypes.bool.isRequired,
|
|
||||||
active: PropTypes.string.isRequired,
|
|
||||||
shouldUpdate: PropTypes.object.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default withStyles(styles)(Main);
|
|
||||||
|
|
@ -16,35 +16,38 @@
|
||||||
|
|
||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import {withStyles} from 'material-ui/styles';
|
|
||||||
import Drawer from 'material-ui/Drawer';
|
|
||||||
import {IconButton} from "material-ui";
|
|
||||||
import List, {ListItem, ListItemText} from 'material-ui/List';
|
|
||||||
import ChevronLeftIcon from 'material-ui-icons/ChevronLeft';
|
|
||||||
|
|
||||||
import {TAGS, DRAWER_WIDTH} from './Common.jsx';
|
import withStyles from 'material-ui/styles/withStyles';
|
||||||
|
import List, {ListItem, ListItemIcon, ListItemText} from 'material-ui/List';
|
||||||
|
import Icon from 'material-ui/Icon';
|
||||||
|
import Transition from 'react-transition-group/Transition';
|
||||||
|
import {Icon as FontAwesome} from 'react-fa'
|
||||||
|
|
||||||
|
import {TAGS, DURATION} from './Common.jsx';
|
||||||
|
|
||||||
|
// menuDefault is the default style of the menu.
|
||||||
|
const menuDefault = {
|
||||||
|
transition: `margin-left ${DURATION}ms`,
|
||||||
|
};
|
||||||
|
// menu Transition is the additional style of the menu corresponding to the transition's state.
|
||||||
|
const menuTransition = {
|
||||||
|
entered: {marginLeft: -200},
|
||||||
|
};
|
||||||
// Styles for the SideBar component.
|
// Styles for the SideBar component.
|
||||||
const styles = theme => ({
|
const styles = theme => ({
|
||||||
drawerPaper: {
|
list: {
|
||||||
position: 'relative',
|
background: theme.palette.background.appBar,
|
||||||
height: '100%',
|
|
||||||
width: DRAWER_WIDTH,
|
|
||||||
},
|
},
|
||||||
drawerHeader: {
|
listItem: {
|
||||||
display: 'flex',
|
minWidth: theme.spacing.unit * 3,
|
||||||
alignItems: 'center',
|
},
|
||||||
justifyContent: 'flex-end',
|
icon: {
|
||||||
padding: '0 8px',
|
fontSize: theme.spacing.unit * 3,
|
||||||
...theme.mixins.toolbar,
|
|
||||||
transitionDuration: {
|
|
||||||
enter: theme.transitions.duration.enteringScreen,
|
|
||||||
exit: theme.transitions.duration.leavingScreen,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// SideBar renders a sidebar component.
|
// SideBar renders the sidebar of the dashboard.
|
||||||
|
@withStyles(styles)
|
||||||
class SideBar extends Component {
|
class SideBar extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
@ -57,50 +60,58 @@ class SideBar extends Component {
|
||||||
const id = TAGS[key].id;
|
const id = TAGS[key].id;
|
||||||
this.clickOn[id] = event => {
|
this.clickOn[id] = event => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
console.log(event.target.key);
|
props.changeContent(id);
|
||||||
this.props.changeContent(id);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
shouldComponentUpdate(nextProps) {
|
||||||
// The classes property is injected by withStyles().
|
return nextProps.opened !== this.props.opened;
|
||||||
const {classes} = this.props;
|
}
|
||||||
|
|
||||||
|
// menu renders the list of the menu items.
|
||||||
|
menu = transitionState => {
|
||||||
|
const {classes} = this.props; // The classes property is injected by withStyles().
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Drawer
|
<div className={classes.list}>
|
||||||
type="persistent"
|
|
||||||
classes={{paper: classes.drawerPaper,}}
|
|
||||||
open={this.props.opened}
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<div className={classes.drawerHeader}>
|
|
||||||
<IconButton onClick={this.props.close}>
|
|
||||||
<ChevronLeftIcon />
|
|
||||||
</IconButton>
|
|
||||||
</div>
|
|
||||||
<List>
|
<List>
|
||||||
{
|
{
|
||||||
Object.values(TAGS).map(tag => {
|
Object.values(TAGS).map(tag => (
|
||||||
return (
|
<ListItem button key={tag.id} onClick={this.clickOn[tag.id]} className={classes.listItem}>
|
||||||
<ListItem button key={tag.id} onClick={this.clickOn[tag.id]}>
|
<ListItemIcon>
|
||||||
<ListItemText primary={tag.title} />
|
<Icon className={classes.icon}>
|
||||||
|
<FontAwesome name={tag.icon} />
|
||||||
|
</Icon>
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText
|
||||||
|
primary={tag.title}
|
||||||
|
style={{
|
||||||
|
...menuDefault,
|
||||||
|
...menuTransition[transitionState],
|
||||||
|
padding: 0,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
);
|
))
|
||||||
})
|
|
||||||
}
|
}
|
||||||
</List>
|
</List>
|
||||||
</div>
|
</div>
|
||||||
</Drawer>
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Transition mountOnEnter in={this.props.opened} timeout={{enter: DURATION}}>
|
||||||
|
{this.menu}
|
||||||
|
</Transition>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SideBar.propTypes = {
|
SideBar.propTypes = {
|
||||||
classes: PropTypes.object.isRequired,
|
|
||||||
opened: PropTypes.bool.isRequired,
|
opened: PropTypes.bool.isRequired,
|
||||||
close: PropTypes.func.isRequired,
|
|
||||||
changeContent: PropTypes.func.isRequired,
|
changeContent: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withStyles(styles)(SideBar);
|
export default SideBar;
|
||||||
|
|
|
||||||
25
dashboard/assets/faOnlyWoffLoader.js
Normal file
25
dashboard/assets/faOnlyWoffLoader.js
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright 2017 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// faOnlyWoffLoader removes the .eot, .ttf, .svg dependencies of the FontAwesome, because they produce unused extra blobs.
|
||||||
|
module.exports = function(content) {
|
||||||
|
content = content.replace(/src.*url(?!.*url.*(\.eot)).*(\.eot)[^;]*;/,'');
|
||||||
|
content = content.replace(/url(?!.*url.*(\.eot)).*(\.eot)[^,]*,/,'');
|
||||||
|
content = content.replace(/url(?!.*url.*(\.ttf)).*(\.ttf)[^,]*,/,'');
|
||||||
|
content = content.replace(/,[^,]*url(?!.*url.*(\.svg)).*(\.svg)[^;]*;/,';');
|
||||||
|
|
||||||
|
return content;
|
||||||
|
};
|
||||||
|
|
@ -15,7 +15,8 @@
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {hydrate} from 'react-dom';
|
import {render} from 'react-dom';
|
||||||
|
|
||||||
import {createMuiTheme, MuiThemeProvider} from 'material-ui/styles';
|
import {createMuiTheme, MuiThemeProvider} from 'material-ui/styles';
|
||||||
|
|
||||||
import Dashboard from './components/Dashboard.jsx';
|
import Dashboard from './components/Dashboard.jsx';
|
||||||
|
|
@ -28,7 +29,7 @@ const theme = createMuiTheme({
|
||||||
});
|
});
|
||||||
|
|
||||||
// Renders the whole dashboard.
|
// Renders the whole dashboard.
|
||||||
hydrate(
|
render(
|
||||||
<MuiThemeProvider theme={theme}>
|
<MuiThemeProvider theme={theme}>
|
||||||
<Dashboard />
|
<Dashboard />
|
||||||
</MuiThemeProvider>,
|
</MuiThemeProvider>,
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,30 @@
|
||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"material-ui": "^1.0.0-beta.21",
|
||||||
|
"material-ui-icons": "^1.0.0-beta.17",
|
||||||
|
"react-fa": "^5.0.0",
|
||||||
|
"react-transition-group": "^2.2.1",
|
||||||
|
"recharts": "^1.0.0-beta.1",
|
||||||
|
|
||||||
|
"classnames": "^2.2.5",
|
||||||
|
"eslint": "^4.11.0",
|
||||||
|
"eslint-plugin-react": "^7.5.1",
|
||||||
|
"prop-types": "^15.6.0",
|
||||||
|
"react": "^16.1.1",
|
||||||
|
"react-dom": "^16.1.1",
|
||||||
|
|
||||||
"babel-core": "^6.26.0",
|
"babel-core": "^6.26.0",
|
||||||
"babel-eslint": "^8.0.1",
|
"babel-eslint": "^8.0.2",
|
||||||
"babel-loader": "^7.1.2",
|
"babel-loader": "^7.1.2",
|
||||||
|
"babel-plugin-transform-decorators-legacy": "^1.3.4",
|
||||||
"babel-preset-env": "^1.6.1",
|
"babel-preset-env": "^1.6.1",
|
||||||
"babel-preset-react": "^6.24.1",
|
"babel-preset-react": "^6.24.1",
|
||||||
"babel-preset-stage-0": "^6.24.1",
|
"babel-preset-stage-0": "^6.24.1",
|
||||||
"classnames": "^2.2.5",
|
"css-loader": "^0.28.7",
|
||||||
"eslint": "^4.5.0",
|
|
||||||
"eslint-plugin-react": "^7.4.0",
|
|
||||||
"material-ui": "^1.0.0-beta.18",
|
|
||||||
"material-ui-icons": "^1.0.0-beta.17",
|
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
"prop-types": "^15.6.0",
|
"style-loader": "^0.19.0",
|
||||||
"recharts": "^1.0.0-beta.0",
|
|
||||||
"react": "^16.0.0",
|
|
||||||
"react-dom": "^16.0.0",
|
|
||||||
"url": "^0.11.0",
|
"url": "^0.11.0",
|
||||||
|
"url-loader": "^0.6.2",
|
||||||
"webpack": "^3.5.5"
|
"webpack": "^3.5.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,14 @@
|
||||||
|
|
||||||
<title>Go Ethereum Dashboard</title>
|
<title>Go Ethereum Dashboard</title>
|
||||||
<link rel="shortcut icon" type="image/ico" href="https://ethereum.org/favicon.ico" />
|
<link rel="shortcut icon" type="image/ico" href="https://ethereum.org/favicon.ico" />
|
||||||
|
<style>
|
||||||
<!-- TODO (kurkomisi): Return to the external libraries to speed up the bundling during development -->
|
::-webkit-scrollbar {
|
||||||
|
width: 16px;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: #212121;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body style="height: 100%; margin: 0">
|
<body style="height: 100%; margin: 0">
|
||||||
<div id="dashboard" style="height: 100%"></div>
|
<div id="dashboard" style="height: 100%"></div>
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
const webpack = require('webpack');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
@ -22,14 +23,34 @@ module.exports = {
|
||||||
path: path.resolve(__dirname, 'public'),
|
path: path.resolve(__dirname, 'public'),
|
||||||
filename: 'bundle.js',
|
filename: 'bundle.js',
|
||||||
},
|
},
|
||||||
|
plugins: [
|
||||||
|
new webpack.optimize.UglifyJsPlugin({
|
||||||
|
comments: false,
|
||||||
|
mangle: false,
|
||||||
|
beautify: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
module: {
|
module: {
|
||||||
loaders: [
|
loaders: [
|
||||||
{
|
{
|
||||||
test: /\.jsx$/, // regexp for JSX files
|
test: /\.jsx$/, // regexp for JSX files
|
||||||
loader: 'babel-loader', // The babel configuration is in the package.json.
|
loader: 'babel-loader',
|
||||||
query: {
|
query: {
|
||||||
presets: ['env', 'react', 'stage-0']
|
plugins: ['transform-decorators-legacy'], // @withStyles, @withTheme
|
||||||
}
|
presets: ['env', 'react', 'stage-0'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /font-awesome\.css$/,
|
||||||
|
use: [
|
||||||
|
'style-loader',
|
||||||
|
'css-loader',
|
||||||
|
path.resolve(__dirname, './faOnlyWoffLoader.js'),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.woff2?$/,
|
||||||
|
loader: 'url-loader',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,9 @@
|
||||||
|
|
||||||
package dashboard
|
package dashboard
|
||||||
|
|
||||||
//go:generate go-bindata -nometadata -o assets.go -prefix assets -pkg dashboard assets/public/...
|
//go:generate ./assets/node_modules/.bin/webpack --config ./assets/webpack.config.js --context ./assets
|
||||||
|
//go:generate gofmt -s -w .
|
||||||
|
//go:generate go-bindata -nometadata -o assets.go -prefix assets -nocompress -pkg dashboard assets/public/...
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -277,6 +279,7 @@ func (db *Dashboard) collectData() {
|
||||||
func (db *Dashboard) collectLogs() {
|
func (db *Dashboard) collectLogs() {
|
||||||
defer db.wg.Done()
|
defer db.wg.Done()
|
||||||
|
|
||||||
|
id := 1
|
||||||
// TODO (kurkomisi): log collection comes here.
|
// TODO (kurkomisi): log collection comes here.
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
|
@ -285,8 +288,9 @@ func (db *Dashboard) collectLogs() {
|
||||||
return
|
return
|
||||||
case <-time.After(db.config.Refresh / 2):
|
case <-time.After(db.config.Refresh / 2):
|
||||||
db.sendToAll(&message{
|
db.sendToAll(&message{
|
||||||
Log: "This is a fake log.",
|
Log: fmt.Sprint(id, ": This is a fake log."),
|
||||||
})
|
})
|
||||||
|
id++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue