Add progress bar to the stats tables
This commit is contained in:
parent
1233901822
commit
6ca881ee86
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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: "%";
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
|
@ -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;
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue