Add progress bar to the stats tables

This commit is contained in:
Ildar Kamalov 2018-10-12 16:02:01 +03:00
parent 1233901822
commit 6ca881ee86
8 changed files with 233 additions and 71 deletions

View File

@ -1,34 +1,60 @@
import React from 'react'; import React, { Component } from 'react';
import ReactTable from 'react-table'; import ReactTable from 'react-table';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import map from 'lodash/map'; import map from 'lodash/map';
import Card from '../ui/Card'; import Card from '../ui/Card';
import Cell from '../ui/Cell';
const Clients = props => ( import { getPercent } from '../../helpers/helpers';
<Card title="Top blocked domains" subtitle="for the last 24 hours" bodyType="card-table" refresh={props.refreshButton}> import { STATUS_COLORS } from '../../helpers/constants';
<ReactTable
data={map(props.topBlockedDomains, (value, prop) => (
{ ip: prop, domain: value }
))}
columns={[{
Header: 'IP',
accessor: 'ip',
}, {
Header: 'Domain name',
accessor: 'domain',
}]}
showPagination={false}
noDataText="No domains found"
minRows={6}
className="-striped -highlight card-table-overflow"
/>
</Card>
);
Clients.propTypes = { class BlockedDomains extends Component {
columns = [{
Header: 'IP',
accessor: 'ip',
}, {
Header: 'Requests count',
accessor: 'domain',
Cell: ({ value }) => {
const {
blockedFiltering,
replacedSafebrowsing,
replacedParental,
} = this.props;
const blocked = blockedFiltering + replacedSafebrowsing + replacedParental;
const percent = getPercent(blocked, value);
return (
<Cell value={value} percent={percent} color={STATUS_COLORS.red} />
);
},
}];
render() {
return (
<Card title="Top blocked domains" subtitle="for the last 24 hours" bodyType="card-table" refresh={this.props.refreshButton}>
<ReactTable
data={map(this.props.topBlockedDomains, (value, prop) => (
{ ip: prop, domain: value }
))}
columns={this.columns}
showPagination={false}
noDataText="No domains found"
minRows={6}
className="-striped -highlight card-table-overflow"
/>
</Card>
);
}
}
BlockedDomains.propTypes = {
topBlockedDomains: PropTypes.object.isRequired, topBlockedDomains: PropTypes.object.isRequired,
refreshButton: PropTypes.node, blockedFiltering: PropTypes.number.isRequired,
replacedSafebrowsing: PropTypes.number.isRequired,
replacedParental: PropTypes.number.isRequired,
refreshButton: PropTypes.node.isRequired,
}; };
export default Clients; export default BlockedDomains;

View File

@ -1,34 +1,62 @@
import React from 'react'; import React, { Component } from 'react';
import ReactTable from 'react-table'; import ReactTable from 'react-table';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import map from 'lodash/map'; import map from 'lodash/map';
import Card from '../ui/Card'; import Card from '../ui/Card';
import Cell from '../ui/Cell';
const Clients = props => ( import { getPercent } from '../../helpers/helpers';
<Card title="Top clients" subtitle="for the last 24 hours" bodyType="card-table" refresh={props.refreshButton}> import { STATUS_COLORS } from '../../helpers/constants';
<ReactTable
data={map(props.topClients, (value, prop) => ( class Clients extends Component {
{ ip: prop, count: value } getPercentColor = (percent) => {
))} if (percent > 50) {
columns={[{ return STATUS_COLORS.green;
Header: 'IP', } else if (percent > 10) {
accessor: 'ip', return STATUS_COLORS.yellow;
}, { }
Header: 'Requests count', return STATUS_COLORS.red;
accessor: 'count', }
}]}
showPagination={false} columns = [{
noDataText="No clients found" Header: 'IP',
minRows={6} accessor: 'ip',
className="-striped -highlight card-table-overflow" }, {
/> Header: 'Requests count',
</Card> accessor: 'count',
); Cell: ({ value }) => {
const percent = getPercent(this.props.dnsQueries, value);
const percentColor = this.getPercentColor(percent);
return (
<Cell value={value} percent={percent} color={percentColor} />
);
},
}];
render() {
return (
<Card title="Top clients" subtitle="for the last 24 hours" bodyType="card-table" refresh={this.props.refreshButton}>
<ReactTable
data={map(this.props.topClients, (value, prop) => (
{ ip: prop, count: value }
))}
columns={this.columns}
showPagination={false}
noDataText="No clients found"
minRows={6}
className="-striped -highlight card-table-overflow"
/>
</Card>
);
}
}
Clients.propTypes = { Clients.propTypes = {
topClients: PropTypes.object.isRequired, topClients: PropTypes.object.isRequired,
refreshButton: PropTypes.node, dnsQueries: PropTypes.number.isRequired,
refreshButton: PropTypes.node.isRequired,
}; };
export default Clients; export default Clients;

View File

@ -88,7 +88,7 @@ Counters.propTypes = {
replacedParental: PropTypes.number.isRequired, replacedParental: PropTypes.number.isRequired,
replacedSafesearch: PropTypes.number.isRequired, replacedSafesearch: PropTypes.number.isRequired,
avgProcessingTime: PropTypes.number.isRequired, avgProcessingTime: PropTypes.number.isRequired,
refreshButton: PropTypes.node, refreshButton: PropTypes.node.isRequired,
}; };
export default Counters; export default Counters;

View File

@ -1,34 +1,62 @@
import React from 'react'; import React, { Component } from 'react';
import ReactTable from 'react-table'; import ReactTable from 'react-table';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import map from 'lodash/map'; import map from 'lodash/map';
import Card from '../ui/Card'; import Card from '../ui/Card';
import Cell from '../ui/Cell';
const QueriedDomains = props => ( import { getPercent } from '../../helpers/helpers';
<Card title="Top queried domains" subtitle="for the last 24 hours" bodyType="card-table" refresh={props.refreshButton}> import { STATUS_COLORS } from '../../helpers/constants';
<ReactTable
data={map(props.topQueriedDomains, (value, prop) => ( class QueriedDomains extends Component {
{ ip: prop, count: value } getPercentColor = (percent) => {
))} if (percent > 10) {
columns={[{ return STATUS_COLORS.red;
Header: 'IP', } else if (percent > 5) {
accessor: 'ip', return STATUS_COLORS.yellow;
}, { }
Header: 'Requests count', return STATUS_COLORS.green;
accessor: 'count', }
}]}
showPagination={false} columns = [{
noDataText="No domains found" Header: 'IP',
minRows={6} accessor: 'ip',
className="-striped -highlight card-table-overflow" }, {
/> Header: 'Requests count',
</Card> accessor: 'count',
); Cell: ({ value }) => {
const percent = getPercent(this.props.dnsQueries, value);
const percentColor = this.getPercentColor(percent);
return (
<Cell value={value} percent={percent} color={percentColor} />
);
},
}];
render() {
return (
<Card title="Top queried domains" subtitle="for the last 24 hours" bodyType="card-table" refresh={this.props.refreshButton}>
<ReactTable
data={map(this.props.topQueriedDomains, (value, prop) => (
{ ip: prop, count: value }
))}
columns={this.columns}
showPagination={false}
noDataText="No domains found"
minRows={6}
className="-striped -highlight card-table-overflow"
/>
</Card>
);
}
}
QueriedDomains.propTypes = { QueriedDomains.propTypes = {
topQueriedDomains: PropTypes.object.isRequired, topQueriedDomains: PropTypes.object.isRequired,
refreshButton: PropTypes.node, dnsQueries: PropTypes.number.isRequired,
refreshButton: PropTypes.node.isRequired,
}; };
export default QueriedDomains; export default QueriedDomains;

View File

@ -61,6 +61,10 @@ class Dashboard extends Component {
<Statistics <Statistics
history={dashboard.statsHistory} history={dashboard.statsHistory}
refreshButton={refreshButton} refreshButton={refreshButton}
dnsQueries={dashboard.stats.dns_queries}
blockedFiltering={dashboard.stats.blocked_filtering}
replacedSafebrowsing={dashboard.stats.replaced_safebrowsing}
replacedParental={dashboard.stats.replaced_parental}
/> />
</div> </div>
} }
@ -81,12 +85,14 @@ class Dashboard extends Component {
<Fragment> <Fragment>
<div className="col-lg-6"> <div className="col-lg-6">
<Clients <Clients
dnsQueries={dashboard.stats.dns_queries}
refreshButton={refreshButton} refreshButton={refreshButton}
topClients={dashboard.topStats.top_clients} topClients={dashboard.topStats.top_clients}
/> />
</div> </div>
<div className="col-lg-6"> <div className="col-lg-6">
<QueriedDomains <QueriedDomains
dnsQueries={dashboard.stats.dns_queries}
refreshButton={refreshButton} refreshButton={refreshButton}
topQueriedDomains={dashboard.topStats.top_queried_domains} topQueriedDomains={dashboard.topStats.top_queried_domains}
/> />
@ -95,6 +101,9 @@ class Dashboard extends Component {
<BlockedDomains <BlockedDomains
refreshButton={refreshButton} refreshButton={refreshButton}
topBlockedDomains={dashboard.topStats.top_blocked_domains} topBlockedDomains={dashboard.topStats.top_blocked_domains}
blockedFiltering={dashboard.stats.blocked_filtering}
replacedSafebrowsing={dashboard.stats.replaced_safebrowsing}
replacedParental={dashboard.stats.replaced_parental}
/> />
</div> </div>
</Fragment> </Fragment>

View File

@ -26,7 +26,6 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 400px;
} }
.card-body--status { .card-body--status {
@ -48,3 +47,40 @@
.card-refresh:focus:active { .card-refresh:focus:active {
background-image: url("data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjI0IiBzdHJva2U9IiNmZmYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIyIiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJtMjMgNHY2aC02Ii8+PHBhdGggZD0ibTEgMjB2LTZoNiIvPjxwYXRoIGQ9Im0zLjUxIDlhOSA5IDAgMCAxIDE0Ljg1LTMuMzZsNC42NCA0LjM2bS0yMiA0IDQuNjQgNC4zNmE5IDkgMCAwIDAgMTQuODUtMy4zNiIvPjwvc3ZnPg=="); background-image: url("data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjI0IiBzdHJva2U9IiNmZmYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIyIiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJtMjMgNHY2aC02Ii8+PHBhdGggZD0ibTEgMjB2LTZoNiIvPjxwYXRoIGQ9Im0zLjUxIDlhOSA5IDAgMCAxIDE0Ljg1LTMuMzZsNC42NCA0LjM2bS0yMiA0IDQuNjQgNC4zNmE5IDkgMCAwIDAgMTQuODUtMy4zNiIvPjwvc3ZnPg==");
} }
.card-title-stats {
color: #9aa0ac;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.card-body-stats {
position: relative;
flex: 1 1 auto;
margin: 0;
padding: 1rem 1.5rem;
}
.card-value-stats {
display: block;
font-size: 2.1rem;
line-height: 2.7rem;
height: 2.7rem;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.card-value-percent {
position: absolute;
top: 15px;
right: 15px;
font-size: 0.9rem;
line-height: 1;
height: auto;
}
.card-value-percent:after {
content: "%";
}

View File

@ -0,0 +1,30 @@
import React from 'react';
import PropTypes from 'prop-types';
const Cell = props => (
<div className="stats__row">
<div className="stats__row-value mb-1">
<strong>{props.value}</strong>
<small className="ml-3 text-muted">
{props.percent}%
</small>
</div>
<div className="progress progress-xs">
<div
className="progress-bar"
style={{
width: `${props.percent}%`,
backgroundColor: props.color,
}}
/>
</div>
</div>
);
Cell.propTypes = {
value: PropTypes.number.isRequired,
percent: PropTypes.number.isRequired,
color: PropTypes.string.isRequired,
};
export default Cell;

View File

@ -75,4 +75,9 @@ export const normalizeFilteringStatus = (filteringStatus) => {
return { enabled, userRules: newUserRules, filters: newFilters }; return { enabled, userRules: newUserRules, filters: newFilters };
}; };
export const getPercent = (amount, number) => round(100 / (amount / number)); export const getPercent = (amount, number) => {
if (amount > 0 && number > 0) {
return round(100 / (amount / number), 2);
}
return 0;
};