From 57c031c1c6eb765ad4d32da6d4744cecfdc19a41 Mon Sep 17 00:00:00 2001
From: Artem Baskal <a.baskal@adguard.com>
Date: Fri, 20 Dec 2019 14:15:57 +0300
Subject: [PATCH 1/2] - client: sort clients table by requests count by default

---
 .../Settings/Clients/AutoClients.js           | 24 ++++++------
 .../Settings/Clients/ClientsTable.js          | 38 +++++++++----------
 .../src/components/Settings/Clients/index.js  |  4 +-
 client/src/helpers/helpers.js                 |  7 ++++
 client/src/reducers/stats.js                  |  2 +
 5 files changed, 40 insertions(+), 35 deletions(-)

diff --git a/client/src/components/Settings/Clients/AutoClients.js b/client/src/components/Settings/Clients/AutoClients.js
index f1ea86bc..4348c62b 100644
--- a/client/src/components/Settings/Clients/AutoClients.js
+++ b/client/src/components/Settings/Clients/AutoClients.js
@@ -11,15 +11,6 @@ import whoisCell from './whoisCell';
 const COLUMN_MIN_WIDTH = 200;
 
 class AutoClients extends Component {
-    getStats = (ip, stats) => {
-        if (stats) {
-            const statsForCurrentIP = stats.find(item => item.name === ip);
-            return statsForCurrentIP && statsForCurrentIP.count;
-        }
-
-        return '';
-    };
-
     columns = [
         {
             Header: this.props.t('table_client'),
@@ -47,11 +38,12 @@ class AutoClients extends Component {
         },
         {
             Header: this.props.t('requests_count'),
-            accessor: 'statistics',
+            accessor: row => this.props.normalizedTopClients[row.ip] || 0,
+            sortMethod: (a, b) => b - a,
+            id: 'statistics',
             minWidth: COLUMN_MIN_WIDTH,
             Cell: (row) => {
-                const clientIP = row.original.ip;
-                const clientStats = clientIP && this.getStats(clientIP, this.props.topClients);
+                const { value: clientStats } = row;
 
                 if (clientStats) {
                     return (
@@ -80,6 +72,12 @@ class AutoClients extends Component {
                 <ReactTable
                     data={autoClients || []}
                     columns={this.columns}
+                    defaultSorted={[
+                        {
+                            id: 'statistics',
+                            asc: true,
+                        },
+                    ]}
                     className="-striped -highlight card-table-overflow"
                     showPagination={true}
                     defaultPageSize={10}
@@ -100,7 +98,7 @@ class AutoClients extends Component {
 AutoClients.propTypes = {
     t: PropTypes.func.isRequired,
     autoClients: PropTypes.array.isRequired,
-    topClients: PropTypes.array.isRequired,
+    normalizedTopClients: PropTypes.object.isRequired,
 };
 
 export default withNamespaces()(AutoClients);
diff --git a/client/src/components/Settings/Clients/ClientsTable.js b/client/src/components/Settings/Clients/ClientsTable.js
index 058cd96d..f21863c9 100644
--- a/client/src/components/Settings/Clients/ClientsTable.js
+++ b/client/src/components/Settings/Clients/ClientsTable.js
@@ -62,15 +62,6 @@ class ClientsTable extends Component {
         };
     };
 
-    getStats = (name, stats) => {
-        if (stats) {
-            const currentStats = stats.find(item => item.info && item.info.name === name);
-            return currentStats && currentStats.count;
-        }
-
-        return '';
-    };
-
     handleDelete = (data) => {
         // eslint-disable-next-line no-alert
         if (window.confirm(this.props.t('client_confirm_delete', { key: data.name }))) {
@@ -138,13 +129,13 @@ class ClientsTable extends Component {
                     <div className="logs__row logs__row--icons">
                         {value && value.length > 0
                             ? value.map(service => (
-                                  <svg
-                                      className="service__icon service__icon--table"
-                                      title={service}
-                                      key={service}
-                                  >
-                                      <use xlinkHref={`#service_${service}`} />
-                                  </svg>
+                                <svg
+                                    className="service__icon service__icon--table"
+                                    title={service}
+                                    key={service}
+                                >
+                                    <use xlinkHref={`#service_${service}`} />
+                                </svg>
                             ))
                             : '–'}
                     </div>
@@ -177,11 +168,12 @@ class ClientsTable extends Component {
         },
         {
             Header: this.props.t('requests_count'),
-            accessor: 'statistics',
+            id: 'statistics',
+            accessor: row => this.props.normalizedTopClients[row.name] || 0,
+            sortMethod: (a, b) => b - a,
             minWidth: 120,
             Cell: (row) => {
-                const { name } = row.original;
-                const clientStats = this.getStats(name, this.props.topClients);
+                const { value: clientStats } = row;
 
                 if (clientStats) {
                     return (
@@ -265,6 +257,12 @@ class ClientsTable extends Component {
                     <ReactTable
                         data={clients || []}
                         columns={this.columns}
+                        defaultSorted={[
+                            {
+                                id: 'statistics',
+                                asc: true,
+                            },
+                        ]}
                         className="-striped -highlight card-table-overflow"
                         showPagination={true}
                         defaultPageSize={10}
@@ -304,7 +302,7 @@ class ClientsTable extends Component {
 ClientsTable.propTypes = {
     t: PropTypes.func.isRequired,
     clients: PropTypes.array.isRequired,
-    topClients: PropTypes.array.isRequired,
+    normalizedTopClients: PropTypes.object.isRequired,
     toggleClientModal: PropTypes.func.isRequired,
     deleteClient: PropTypes.func.isRequired,
     addClient: PropTypes.func.isRequired,
diff --git a/client/src/components/Settings/Clients/index.js b/client/src/components/Settings/Clients/index.js
index 5b656d88..667441ef 100644
--- a/client/src/components/Settings/Clients/index.js
+++ b/client/src/components/Settings/Clients/index.js
@@ -33,7 +33,7 @@ class Clients extends Component {
                     <Fragment>
                         <ClientsTable
                             clients={dashboard.clients}
-                            topClients={stats.topClients}
+                            normalizedTopClients={stats.normalizedTopClients}
                             isModalOpen={clients.isModalOpen}
                             modalClientName={clients.modalClientName}
                             modalType={clients.modalType}
@@ -47,7 +47,7 @@ class Clients extends Component {
                         />
                         <AutoClients
                             autoClients={dashboard.autoClients}
-                            topClients={stats.topClients}
+                            normalizedTopClients={stats.normalizedTopClients}
                         />
                     </Fragment>
                 )}
diff --git a/client/src/helpers/helpers.js b/client/src/helpers/helpers.js
index d7c78fc2..8d68794c 100644
--- a/client/src/helpers/helpers.js
+++ b/client/src/helpers/helpers.js
@@ -261,6 +261,13 @@ export const redirectToCurrentProtocol = (values, httpPort = 80) => {
 
 export const normalizeTextarea = text => text && text.replace(/[;, ]/g, '\n').split('\n').filter(n => n);
 
+export const normalizeTopClients = clients => clients.reduce((accumulator, clientObj) => {
+    const { name, count } = clientObj;
+    const idToCountMap = accumulator;
+    idToCountMap[name] = count;
+    return idToCountMap;
+}, {});
+
 export const getClientInfo = (clients, ip) => {
     const client = clients
         .find(item => item.ip_addrs && item.ip_addrs.find(clientIp => clientIp === ip));
diff --git a/client/src/reducers/stats.js b/client/src/reducers/stats.js
index 88c33a12..9a74c1b1 100644
--- a/client/src/reducers/stats.js
+++ b/client/src/reducers/stats.js
@@ -1,4 +1,5 @@
 import { handleActions } from 'redux-actions';
+import { normalizeTopClients } from '../helpers/helpers';
 
 import * as actions from '../actions/stats';
 
@@ -64,6 +65,7 @@ const stats = handleActions(
                 replacedSafebrowsing,
                 topBlockedDomains,
                 topClients,
+                normalizedTopClients: normalizeTopClients(topClients),
                 topQueriedDomains,
                 numBlockedFiltering,
                 numDnsQueries,

From ca79fc98f5d43a5ca1955e8b0056edc3a6ee9b61 Mon Sep 17 00:00:00 2001
From: Artem Baskal <a.baskal@adguard.com>
Date: Fri, 20 Dec 2019 16:22:38 +0300
Subject: [PATCH 2/2] change code style in reduce

---
 client/src/helpers/helpers.js | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/client/src/helpers/helpers.js b/client/src/helpers/helpers.js
index 8d68794c..8f9556af 100644
--- a/client/src/helpers/helpers.js
+++ b/client/src/helpers/helpers.js
@@ -263,9 +263,9 @@ export const normalizeTextarea = text => text && text.replace(/[;, ]/g, '\n').sp
 
 export const normalizeTopClients = clients => clients.reduce((accumulator, clientObj) => {
     const { name, count } = clientObj;
-    const idToCountMap = accumulator;
-    idToCountMap[name] = count;
-    return idToCountMap;
+    // eslint-disable-next-line no-param-reassign
+    accumulator[name] = count;
+    return accumulator;
 }, {});
 
 export const getClientInfo = (clients, ip) => {