+ client: add runtime clients table

This commit is contained in:
Ildar Kamalov 2019-05-23 14:14:22 +03:00 committed by Simon Zolin
parent cbef338592
commit 8a8c7329f7
8 changed files with 175 additions and 22 deletions

View File

@ -286,7 +286,9 @@
"client_deleted": "Client \"{{key}}\" successfully deleted", "client_deleted": "Client \"{{key}}\" successfully deleted",
"client_added": "Client \"{{key}}\" successfully added", "client_added": "Client \"{{key}}\" successfully added",
"client_updated": "Client \"{{key}}\" successfully updated", "client_updated": "Client \"{{key}}\" successfully updated",
"table_statistics": "Statistics (last 24 hours)", "table_statistics": "Requests count (last 24 hours)",
"clients_not_found": "No clients found", "clients_not_found": "No clients found",
"client_confirm_delete": "Are you sure you want to delete client \"{{key}}\"?" "client_confirm_delete": "Are you sure you want to delete client \"{{key}}\"?",
"auto_clients_title": "Clients (runtime)",
"auto_clients_desc": "Data on the clients that use AdGuard Home, but not stored in the configuration"
} }

View File

@ -213,9 +213,14 @@ export const getClientsSuccess = createAction('GET_CLIENTS_SUCCESS');
export const getClients = () => async (dispatch) => { export const getClients = () => async (dispatch) => {
dispatch(getClientsRequest()); dispatch(getClientsRequest());
try { try {
const clients = await apiClient.getClients(); const data = await apiClient.getClients();
const sortedClients = sortClients(clients); const sortedClients = data.clients && sortClients(data.clients);
dispatch(getClientsSuccess(sortedClients)); const sortedAutoClients = data.auto_clients && sortClients(data.auto_clients);
dispatch(getClientsSuccess({
clients: sortedClients || [],
autoClients: sortedAutoClients || [],
}));
} catch (error) { } catch (error) {
dispatch(addErrorToast({ error })); dispatch(addErrorToast({ error }));
dispatch(getClientsFailure()); dispatch(getClientsFailure());

View File

@ -24,7 +24,8 @@ class Clients extends Component {
Header: 'IP', Header: 'IP',
accessor: 'ip', accessor: 'ip',
Cell: ({ value }) => { Cell: ({ value }) => {
const clientName = getClientName(this.props.clients, value); const clientName = getClientName(this.props.clients, value)
|| getClientName(this.props.autoClients, value);
let client; let client;
if (clientName) { if (clientName) {
@ -79,6 +80,7 @@ Clients.propTypes = {
dnsQueries: PropTypes.number.isRequired, dnsQueries: PropTypes.number.isRequired,
refreshButton: PropTypes.node.isRequired, refreshButton: PropTypes.node.isRequired,
clients: PropTypes.array.isRequired, clients: PropTypes.array.isRequired,
autoClients: PropTypes.array.isRequired,
t: PropTypes.func, t: PropTypes.func,
}; };

View File

@ -96,6 +96,7 @@ class Dashboard extends Component {
refreshButton={refreshButton} refreshButton={refreshButton}
topClients={dashboard.topStats.top_clients} topClients={dashboard.topStats.top_clients}
clients={dashboard.clients} clients={dashboard.clients}
autoClients={dashboard.autoClients}
/> />
</div> </div>
<div className="col-lg-6"> <div className="col-lg-6">

View File

@ -196,7 +196,8 @@ class Logs extends Component {
Cell: (row) => { Cell: (row) => {
const { reason } = row.original; const { reason } = row.original;
const isFiltered = row ? reason.indexOf('Filtered') === 0 : false; const isFiltered = row ? reason.indexOf('Filtered') === 0 : false;
const clientName = getClientName(dashboard.clients, row.value); const clientName = getClientName(dashboard.clients, row.value)
|| getClientName(dashboard.autoClients, row.value);
let client; let client;
if (clientName) { if (clientName) {

View File

@ -0,0 +1,131 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withNamespaces } from 'react-i18next';
import ReactTable from 'react-table';
import { CLIENT_ID } from '../../../helpers/constants';
import Card from '../../ui/Card';
class AutoClients extends Component {
getClient = (name, clients) => {
const client = clients.find(item => name === item.name);
if (client) {
const identifier = client.mac ? CLIENT_ID.MAC : CLIENT_ID.IP;
return {
identifier,
use_global_settings: true,
...client,
};
}
return {
identifier: 'ip',
use_global_settings: true,
};
};
getStats = (ip, stats) => {
if (stats && stats.top_clients) {
return stats.top_clients[ip];
}
return '';
};
columns = [
{
Header: this.props.t('table_client'),
accessor: 'ip',
Cell: (row) => {
if (row.value) {
return (
<div className="logs__row logs__row--overflow">
<span className="logs__text" title={row.value}>
{row.value} <em>(IP)</em>
</span>
</div>
);
} else if (row.original && row.original.mac) {
return (
<div className="logs__row logs__row--overflow">
<span className="logs__text" title={row.original.mac}>
{row.original.mac} <em>(MAC)</em>
</span>
</div>
);
}
return '';
},
},
{
Header: this.props.t('table_name'),
accessor: 'name',
Cell: ({ value }) => (
<div className="logs__row logs__row--overflow">
<span className="logs__text" title={value}>
{value}
</span>
</div>
),
},
{
Header: this.props.t('table_statistics'),
accessor: 'statistics',
Cell: (row) => {
const clientIP = row.original.ip;
const clientStats = clientIP && this.getStats(clientIP, this.props.topStats);
if (clientStats) {
return (
<div className="logs__row">
<div className="logs__text" title={clientStats}>
{clientStats}
</div>
</div>
);
}
return '';
},
},
];
render() {
const { t, autoClients } = this.props;
return (
<Card
title={t('auto_clients_title')}
subtitle={t('auto_clients_desc')}
bodyType="card-body box-body--settings"
>
<ReactTable
data={autoClients || []}
columns={this.columns}
className="-striped -highlight card-table-overflow"
showPagination={true}
defaultPageSize={10}
minRows={5}
previousText={t('previous_btn')}
nextText={t('next_btn')}
loadingText={t('loading_table_status')}
pageText={t('page_table_footer_text')}
ofText={t('of_table_footer_text')}
rowsText={t('rows_table_footer_text')}
noDataText={t('clients_not_found')}
/>
</Card>
);
}
}
AutoClients.propTypes = {
t: PropTypes.func.isRequired,
autoClients: PropTypes.array.isRequired,
topStats: PropTypes.object.isRequired,
};
export default withNamespaces()(AutoClients);

View File

@ -1,14 +1,17 @@
import React, { Component, Fragment } from 'react'; import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { withNamespaces, Trans } from 'react-i18next'; import { withNamespaces, Trans } from 'react-i18next';
import Upstream from './Upstream'; import Upstream from './Upstream';
import Dhcp from './Dhcp'; import Dhcp from './Dhcp';
import Encryption from './Encryption'; import Encryption from './Encryption';
import Clients from './Clients'; import Clients from './Clients';
import AutoClients from './Clients/AutoClients';
import Checkbox from '../ui/Checkbox'; import Checkbox from '../ui/Checkbox';
import Loading from '../ui/Loading'; import Loading from '../ui/Loading';
import PageTitle from '../ui/PageTitle'; import PageTitle from '../ui/PageTitle';
import Card from '../ui/Card'; import Card from '../ui/Card';
import './Settings.css'; import './Settings.css';
class Settings extends Component { class Settings extends Component {
@ -93,6 +96,7 @@ class Settings extends Component {
processingSetUpstream={settings.processingSetUpstream} processingSetUpstream={settings.processingSetUpstream}
/> />
{!dashboard.processingTopStats && !dashboard.processingClients && ( {!dashboard.processingTopStats && !dashboard.processingClients && (
<Fragment>
<Clients <Clients
clients={dashboard.clients} clients={dashboard.clients}
topStats={dashboard.topStats} topStats={dashboard.topStats}
@ -107,6 +111,11 @@ class Settings extends Component {
processingDeleting={clients.processingDeleting} processingDeleting={clients.processingDeleting}
processingUpdating={clients.processingUpdating} processingUpdating={clients.processingUpdating}
/> />
<AutoClients
autoClients={dashboard.autoClients}
topStats={dashboard.topStats}
/>
</Fragment>
)} )}
<Encryption <Encryption
encryption={this.props.encryption} encryption={this.props.encryption}

View File

@ -185,7 +185,8 @@ const dashboard = handleActions({
[actions.getClientsSuccess]: (state, { payload }) => { [actions.getClientsSuccess]: (state, { payload }) => {
const newState = { const newState = {
...state, ...state,
clients: payload, clients: payload.clients,
autoClients: payload.autoClients,
processingClients: false, processingClients: false,
}; };
return newState; return newState;
@ -210,6 +211,7 @@ const dashboard = handleActions({
dnsAddresses: [], dnsAddresses: [],
dnsVersion: '', dnsVersion: '',
clients: [], clients: [],
autoClients: [],
topStats: [], topStats: [],
}); });