Replace the main Statistics graph with 4 blocks instead

This commit is contained in:
Ildar Kamalov 2018-10-12 15:23:21 +03:00
parent 599426a1f9
commit bc11f872fa
5 changed files with 190 additions and 58 deletions
client/src

View File

@ -1,59 +1,109 @@
import React from 'react'; import React, { Component } from 'react';
import { ResponsiveLine } from '@nivo/line';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Card from '../ui/Card'; import Card from '../ui/Card';
import Line from '../ui/Line';
const Statistics = props => ( import { getPercent } from '../../helpers/helpers';
<Card title="Statistics" subtitle="for the last 24 hours" bodyType="card-graph" refresh={props.refreshButton}> import { STATUS_COLORS } from '../../helpers/constants';
{props.history ?
<ResponsiveLine class Statistics extends Component {
data={props.history} render() {
margin={{ const {
top: 50, dnsQueries,
right: 40, blockedFiltering,
bottom: 80, replacedSafebrowsing,
left: 80, replacedParental,
}} } = this.props;
minY="auto"
stacked={false} const filteringData = [this.props.history[1]];
curve='monotoneX' const queriesData = [this.props.history[2]];
axisBottom={{ const parentalData = [this.props.history[3]];
orient: 'bottom', const safebrowsingData = [this.props.history[4]];
tickSize: 5,
tickPadding: 5, return (
tickRotation: -45, <div className="row">
legendOffset: 50, <div className="col-sm-6 col-lg-3">
legendPosition: 'center', <Card bodyType="card-wrap">
}} <div className="card-body-stats">
axisLeft={{ <div className="card-value card-value-stats text-blue">
orient: 'left', {dnsQueries}
tickSize: 5, </div>
tickPadding: 5, <div className="card-title-stats">
tickRotation: 0, DNS Queries
legendOffset: -40, </div>
legendPosition: 'center', </div>
}} <div className="card-chart-bg">
enableArea={true} <Line data={queriesData} color={STATUS_COLORS.blue}/>
dotSize={10} </div>
dotColor="inherit:darker(0.3)" </Card>
dotBorderWidth={2} </div>
dotBorderColor="#ffffff" <div className="col-sm-6 col-lg-3">
dotLabel="y" <Card bodyType="card-wrap">
dotLabelYOffset={-12} <div className="card-body-stats">
animate={true} <div className="card-value card-value-stats text-red">
motionStiffness={90} {blockedFiltering}
motionDamping={15} </div>
/> <div className="card-value card-value-percent text-red">
: {getPercent(dnsQueries, blockedFiltering)}
<h2 className="text-muted">Empty data</h2> </div>
} <div className="card-title-stats">
</Card> Blocked by Filters
); </div>
</div>
<div className="card-chart-bg">
<Line data={filteringData} color={STATUS_COLORS.red}/>
</div>
</Card>
</div>
<div className="col-sm-6 col-lg-3">
<Card bodyType="card-wrap">
<div className="card-body-stats">
<div className="card-value card-value-stats text-green">
{replacedSafebrowsing}
</div>
<div className="card-value card-value-percent text-green">
{getPercent(dnsQueries, replacedSafebrowsing)}
</div>
<div className="card-title-stats">
Blocked malware/phishing
</div>
</div>
<div className="card-chart-bg">
<Line data={safebrowsingData} color={STATUS_COLORS.green}/>
</div>
</Card>
</div>
<div className="col-sm-6 col-lg-3">
<Card bodyType="card-wrap">
<div className="card-body-stats">
<div className="card-value card-value-stats text-yellow">
{replacedParental}
</div>
<div className="card-value card-value-percent text-yellow">
{getPercent(dnsQueries, replacedParental)}
</div>
<div className="card-title-stats">
Blocked adult websites
</div>
</div>
<div className="card-chart-bg">
<Line data={parentalData} color={STATUS_COLORS.yellow}/>
</div>
</Card>
</div>
</div>
);
}
}
Statistics.propTypes = { Statistics.propTypes = {
history: PropTypes.array.isRequired, history: PropTypes.array.isRequired,
refreshButton: PropTypes.node, dnsQueries: PropTypes.number.isRequired,
blockedFiltering: PropTypes.number.isRequired,
replacedSafebrowsing: PropTypes.number.isRequired,
replacedParental: PropTypes.number.isRequired,
refreshButton: PropTypes.node.isRequired,
}; };
export default Statistics; export default Statistics;

View File

@ -0,0 +1,9 @@
.line__tooltip {
padding: 2px 10px 7px;
line-height: 1.1;
color: #fff;
}
.line__tooltip-text {
font-size: 0.7rem;
}

View File

@ -0,0 +1,62 @@
import React from 'react';
import PropTypes from 'prop-types';
import { ResponsiveLine } from '@nivo/line';
import './Line.css';
const Line = props => (
props.data &&
<ResponsiveLine
data={props.data}
margin={{
top: 15,
right: 0,
bottom: 1,
left: 0,
}}
minY="auto"
stacked={false}
curve='linear'
axisBottom={{
tickSize: 0,
tickPadding: 0,
}}
axisLeft={{
tickSize: 0,
tickPadding: 0,
}}
enableGridX={false}
enableGridY={false}
enableDots={false}
enableArea={true}
animate={false}
colorBy={() => (props.color)}
tooltip={slice => (
<div>
{slice.data.map(d => (
<div key={d.serie.id} className="line__tooltip">
<span className="line__tooltip-text">
{d.data.y}
</span>
</div>
))}
</div>
)}
theme={{
tooltip: {
container: {
padding: '0',
background: '#333',
borderRadius: '4px',
},
},
}}
/>
);
Line.propTypes = {
data: PropTypes.array.isRequired,
color: PropTypes.string.isRequired,
};
export default Line;

View File

@ -1 +1,17 @@
export const R_URL_REQUIRES_PROTOCOL = /^https?:\/\/\w[\w_\-.]*\.[a-z]{2,8}[^\s]*$/; export const R_URL_REQUIRES_PROTOCOL = /^https?:\/\/\w[\w_\-.]*\.[a-z]{2,8}[^\s]*$/;
export const STATS_NAMES = {
avg_processing_time: 'Average processing time',
blocked_filtering: 'Blocked by filters',
dns_queries: 'DNS queries',
replaced_parental: 'Blocked adult websites',
replaced_safebrowsing: 'Blocked malware/phishing',
replaced_safesearch: 'Enforced safe search',
};
export const STATUS_COLORS = {
blue: '#467fcf',
red: '#cd201f',
green: '#5eba00',
yellow: '#f1c40f',
};

View File

@ -4,6 +4,8 @@ import subHours from 'date-fns/sub_hours';
import addHours from 'date-fns/add_hours'; import addHours from 'date-fns/add_hours';
import round from 'lodash/round'; import round from 'lodash/round';
import { STATS_NAMES } from './constants';
const formatTime = (time) => { const formatTime = (time) => {
const parsedTime = dateParse(time); const parsedTime = dateParse(time);
return dateFormat(parsedTime, 'HH:mm:ss'); return dateFormat(parsedTime, 'HH:mm:ss');
@ -34,15 +36,6 @@ export const normalizeLogs = logs => logs.map((log) => {
}; };
}); });
const STATS_NAMES = {
avg_processing_time: 'Average processing time',
blocked_filtering: 'Blocked by filters',
dns_queries: 'DNS queries',
replaced_parental: 'Blocked adult websites',
replaced_safebrowsing: 'Blocked malware/phishing',
replaced_safesearch: 'Enforced safe search',
};
export const normalizeHistory = history => Object.keys(history).map((key) => { export const normalizeHistory = history => Object.keys(history).map((key) => {
let id = STATS_NAMES[key]; let id = STATS_NAMES[key];
if (!id) { if (!id) {
@ -81,3 +74,5 @@ export const normalizeFilteringStatus = (filteringStatus) => {
const newUserRules = Array.isArray(userRules) ? userRules.join('\n') : ''; const newUserRules = Array.isArray(userRules) ? userRules.join('\n') : '';
return { enabled, userRules: newUserRules, filters: newFilters }; return { enabled, userRules: newUserRules, filters: newFilters };
}; };
export const getPercent = (amount, number) => round(100 / (amount / number));