dashboard: minor design change

This commit is contained in:
Kurkó Mihály 2017-11-22 12:46:18 +02:00 committed by Péter Szilágyi
parent 91c3362315
commit 23bce8353a
No known key found for this signature in database
GPG key ID: E9AE538CEDF8293D
17 changed files with 38813 additions and 344 deletions

View file

@ -22,10 +22,9 @@ $ (cd dashboard/assets && ./node_modules/.bin/webpack --watch)
$ 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
```

File diff suppressed because one or more lines are too long

View 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;

View 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;

View file

@ -22,15 +22,16 @@ export const LIMIT = {
traffic: 200, // Maximum number of traffic data samples.
log: 200, // Maximum number of logs.
};
// The sidebar menu and the main content are rendered based on these elements.
export const TAGS = (() => {
const T = {
home: { title: "Home", },
chain: { title: "Chain", },
transactions: { title: "Transactions", },
network: { title: "Network", },
system: { title: "System", },
logs: { title: "Logs", },
home: { title: "Home", icon: "home", },
chain: { title: "Chain", icon: "link", },
transactions: { title: "Transactions", icon: "credit-card", },
network: { title: "Network", icon: "globe", },
system: { title: "System", icon: "tachometer", },
logs: { title: "Logs", icon: "list", },
};
// 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.
@ -48,5 +49,4 @@ export const DATA_KEYS = (() => {
return DK;
})();
// Temporary - taken from Material-UI
export const DRAWER_WIDTH = 240;
export const DURATION = 200;

View 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;

View file

@ -15,27 +15,29 @@
// 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';
import SideBar from './SideBar.jsx';
import withStyles from 'material-ui/styles/withStyles';
import Header from './Header.jsx';
import Main from "./Main.jsx";
import {isNullOrUndefined, LIMIT, TAGS, DATA_KEYS,} from "./Common.jsx";
import Body from './Body.jsx';
import {isNullOrUndefined, LIMIT, TAGS, DATA_KEYS} from "./Common.jsx";
// Styles for the Dashboard component.
const styles = theme => ({
appFrame: {
position: 'relative',
dashboard: {
display: 'flex',
flexFlow: 'column',
width: '100%',
height: '100%',
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.
// When there is an incoming message, updates the page's content correspondingly.
@withStyles(styles)
class Dashboard extends Component {
constructor(props) {
super(props);
@ -45,7 +47,7 @@ class Dashboard extends Component {
memory: [],
traffic: [],
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 = msg => {
console.log(msg);
this.setState(prevState => {
let newState = [];
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 = active => {
this.setState(prevState => prevState.active !== active ? {active: active} : {});
// changeContent sets the active label, which is used at the content rendering.
changeContent = newActive => {
this.setState(prevState => prevState.active !== newActive ? {active: newActive} : {});
};
openSideBar = () => {
@ -135,22 +136,18 @@ class Dashboard extends Component {
};
render() {
// The classes property is injected by withStyles().
const {classes} = this.props;
const {classes} = this.props; // The classes property is injected by withStyles().
return (
<div className={classes.appFrame}>
<div className={classes.dashboard}>
<Header
opened={this.state.sideBar}
open={this.openSideBar}
openSideBar={this.openSideBar}
closeSideBar={this.closeSideBar}
/>
<SideBar
<Body
opened={this.state.sideBar}
close={this.closeSideBar}
changeContent={this.changeContent}
/>
<Main
opened={this.state.sideBar}
active={this.state.active}
memory={this.state.memory}
traffic={this.state.traffic}
@ -162,8 +159,4 @@ class Dashboard extends Component {
}
}
Dashboard.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(Dashboard);
export default Dashboard;

View file

@ -16,61 +16,76 @@
import React, {Component} from 'react';
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 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 IconButton from 'material-ui/IconButton';
import MenuIcon from 'material-ui-icons/Menu';
import ChevronLeftIcon from 'material-ui-icons/ChevronLeft';
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.
const styles = theme => ({
appBar: {
position: 'absolute',
transition: theme.transitions.create(['margin', 'width'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
header: {
backgroundColor: theme.palette.background.appBar,
color: theme.palette.getContrastText(theme.palette.background.appBar),
zIndex: theme.zIndex.appBar,
},
appBarShift: {
marginLeft: DRAWER_WIDTH,
width: `calc(100% - ${DRAWER_WIDTH}px)`,
transition: theme.transitions.create(['margin', 'width'], {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen,
}),
toolbar: {
paddingLeft: theme.spacing.unit,
paddingRight: theme.spacing.unit,
},
menuButton: {
marginLeft: 12,
marginRight: 20,
},
hide: {
display: 'none',
mainText: {
paddingLeft: theme.spacing.unit,
},
});
// 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 {
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() {
// The classes property is injected by withStyles().
const {classes} = this.props;
const {classes, opened} = this.props; // The classes property is injected by withStyles().
return (
<AppBar className={classNames(classes.appBar, this.props.opened && classes.appBarShift)}>
<Toolbar disableGutters={!this.props.opened}>
<IconButton
color="contrast"
aria-label="open drawer"
onClick={this.props.open}
className={classNames(classes.menuButton, this.props.opened && classes.hide)}
>
<MenuIcon />
</IconButton>
<Typography type="title" color="inherit" noWrap>
Go Ethereum Dashboard
<AppBar position="static" className={classes.header}>
<Toolbar className={classes.toolbar}>
<Transition mountOnEnter in={opened} timeout={{enter: DURATION}}>
{this.arrowButton}
</Transition>
<Typography type="title" color="inherit" noWrap className={classes.mainText}>
PoC Go Ethereum Dashboard
</Typography>
</Toolbar>
</AppBar>
@ -79,9 +94,9 @@ class Header extends Component {
}
Header.propTypes = {
classes: PropTypes.object.isRequired,
opened: PropTypes.bool.isRequired,
open: PropTypes.func.isRequired,
opened: PropTypes.bool.isRequired,
openSideBar: PropTypes.func.isRequired,
closeSideBar: PropTypes.func.isRequired,
};
export default withStyles(styles)(Header);
export default Header;

View file

@ -16,65 +16,48 @@
import React, {Component} from 'react';
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";
// 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,
};
// Home renders the home component.
// Home renders the home content.
@withTheme()
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) {
return !isNullOrUndefined(nextProps.shouldUpdate[DATA_KEYS.memory]) ||
!isNullOrUndefined(nextProps.shouldUpdate[DATA_KEYS.traffic]);
}
render() {
const {theme} = this.props;
const memoryColor = theme.palette.primary[300];
const trafficColor = theme.palette.secondary[300];
const {memory, traffic} = this.props;
return (
<ChartGrid spacing={24}>
<AreaChart xs={6} height={300} values={this.props.memory}>
<AreaChart xs={6} height={300} values={memory}>
<YAxis />
<Area type="monotone" dataKey="value" stroke={memoryColor} fill={memoryColor} />
<Area type="monotone" dataKey="value" stroke={this.memoryColor} fill={this.memoryColor} />
</AreaChart>
<LineChart xs={6} height={300} values={this.props.traffic}>
<Line type="monotone" dataKey="value" stroke={trafficColor} dot={false} />
<LineChart xs={6} height={300} values={traffic}>
<Line type="monotone" dataKey="value" stroke={this.trafficColor} dot={false} />
</LineChart>
<LineChart xs={6} height={300} values={this.props.memory}>
<LineChart xs={6} height={300} values={memory}>
<YAxis />
<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>
<AreaChart xs={6} height={300} values={this.props.traffic}>
<AreaChart xs={6} height={300} values={traffic}>
<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>
</ChartGrid>
);
@ -82,8 +65,7 @@ class Home extends Component {
}
Home.propTypes = {
theme: PropTypes.object.isRequired,
shouldUpdate: PropTypes.object.isRequired,
};
export default withTheme()(Home);
export default Home;

View file

@ -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);

View file

@ -16,35 +16,38 @@
import React, {Component} from 'react';
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.
const styles = theme => ({
drawerPaper: {
position: 'relative',
height: '100%',
width: DRAWER_WIDTH,
list: {
background: theme.palette.background.appBar,
},
drawerHeader: {
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
padding: '0 8px',
...theme.mixins.toolbar,
transitionDuration: {
enter: theme.transitions.duration.enteringScreen,
exit: theme.transitions.duration.leavingScreen,
}
listItem: {
minWidth: theme.spacing.unit * 3,
},
icon: {
fontSize: theme.spacing.unit * 3,
},
});
// SideBar renders a sidebar component.
// SideBar renders the sidebar of the dashboard.
@withStyles(styles)
class SideBar extends Component {
constructor(props) {
super(props);
@ -57,50 +60,58 @@ class SideBar extends Component {
const id = TAGS[key].id;
this.clickOn[id] = event => {
event.preventDefault();
console.log(event.target.key);
this.props.changeContent(id);
props.changeContent(id);
};
}
}
render() {
// The classes property is injected by withStyles().
const {classes} = this.props;
shouldComponentUpdate(nextProps) {
return nextProps.opened !== this.props.opened;
}
// menu renders the list of the menu items.
menu = transitionState => {
const {classes} = this.props; // The classes property is injected by withStyles().
return (
<Drawer
type="persistent"
classes={{paper: classes.drawerPaper,}}
open={this.props.opened}
>
<div>
<div className={classes.drawerHeader}>
<IconButton onClick={this.props.close}>
<ChevronLeftIcon />
</IconButton>
</div>
<List>
{
Object.values(TAGS).map(tag => {
return (
<ListItem button key={tag.id} onClick={this.clickOn[tag.id]}>
<ListItemText primary={tag.title} />
</ListItem>
);
})
}
</List>
</div>
</Drawer>
<div className={classes.list}>
<List>
{
Object.values(TAGS).map(tag => (
<ListItem button key={tag.id} onClick={this.clickOn[tag.id]} className={classes.listItem}>
<ListItemIcon>
<Icon className={classes.icon}>
<FontAwesome name={tag.icon} />
</Icon>
</ListItemIcon>
<ListItemText
primary={tag.title}
style={{
...menuDefault,
...menuTransition[transitionState],
padding: 0,
}}
/>
</ListItem>
))
}
</List>
</div>
);
};
render() {
return (
<Transition mountOnEnter in={this.props.opened} timeout={{enter: DURATION}}>
{this.menu}
</Transition>
);
}
}
SideBar.propTypes = {
classes: PropTypes.object.isRequired,
opened: PropTypes.bool.isRequired,
close: PropTypes.func.isRequired,
changeContent: PropTypes.func.isRequired,
};
export default withStyles(styles)(SideBar);
export default SideBar;

View 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;
};

View file

@ -15,7 +15,8 @@
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
import React from 'react';
import {hydrate} from 'react-dom';
import {render} from 'react-dom';
import {createMuiTheme, MuiThemeProvider} from 'material-ui/styles';
import Dashboard from './components/Dashboard.jsx';
@ -28,7 +29,7 @@ const theme = createMuiTheme({
});
// Renders the whole dashboard.
hydrate(
render(
<MuiThemeProvider theme={theme}>
<Dashboard />
</MuiThemeProvider>,

View file

@ -1,22 +1,30 @@
{
"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-eslint": "^8.0.1",
"babel-eslint": "^8.0.2",
"babel-loader": "^7.1.2",
"babel-plugin-transform-decorators-legacy": "^1.3.4",
"babel-preset-env": "^1.6.1",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-0": "^6.24.1",
"classnames": "^2.2.5",
"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",
"css-loader": "^0.28.7",
"path": "^0.12.7",
"prop-types": "^15.6.0",
"recharts": "^1.0.0-beta.0",
"react": "^16.0.0",
"react-dom": "^16.0.0",
"style-loader": "^0.19.0",
"url": "^0.11.0",
"url-loader": "^0.6.2",
"webpack": "^3.5.5"
}
}

View file

@ -6,9 +6,15 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Go Ethereum Dashboard</title>
<link rel="shortcut icon" type="image/ico" href="https://ethereum.org/favicon.ico"/>
<!-- TODO (kurkomisi): Return to the external libraries to speed up the bundling during development -->
<link rel="shortcut icon" type="image/ico" href="https://ethereum.org/favicon.ico" />
<style>
::-webkit-scrollbar {
width: 16px;
}
::-webkit-scrollbar-thumb {
background: #212121;
}
</style>
</head>
<body style="height: 100%; margin: 0">
<div id="dashboard" style="height: 100%"></div>

View file

@ -14,6 +14,7 @@
// 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/>.
const webpack = require('webpack');
const path = require('path');
module.exports = {
@ -22,14 +23,34 @@ module.exports = {
path: path.resolve(__dirname, 'public'),
filename: 'bundle.js',
},
plugins: [
new webpack.optimize.UglifyJsPlugin({
comments: false,
mangle: false,
beautify: true,
}),
],
module: {
loaders: [
{
test: /\.jsx$/, // regexp for JSX files
loader: 'babel-loader', // The babel configuration is in the package.json.
query: {
presets: ['env', 'react', 'stage-0']
}
test: /\.jsx$/, // regexp for JSX files
loader: 'babel-loader',
query: {
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',
},
],
},

View file

@ -16,7 +16,9 @@
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 (
"fmt"
@ -277,6 +279,7 @@ func (db *Dashboard) collectData() {
func (db *Dashboard) collectLogs() {
defer db.wg.Done()
id := 1
// TODO (kurkomisi): log collection comes here.
for {
select {
@ -285,8 +288,9 @@ func (db *Dashboard) collectLogs() {
return
case <-time.After(db.config.Refresh / 2):
db.sendToAll(&message{
Log: "This is a fake log.",
Log: fmt.Sprint(id, ": This is a fake log."),
})
id++
}
}
}