2019-06-03 12:41:45 +00:00
|
|
|
|
import React, { Component, Fragment } from 'react';
|
|
|
|
|
import PropTypes from 'prop-types';
|
2020-05-22 14:06:05 +00:00
|
|
|
|
import { Trans, withTranslation } from 'react-i18next';
|
2019-06-03 12:41:45 +00:00
|
|
|
|
import ReactTable from 'react-table';
|
|
|
|
|
|
2019-11-28 11:47:06 +00:00
|
|
|
|
import { MODAL_TYPE } from '../../../helpers/constants';
|
2020-07-15 09:35:37 +00:00
|
|
|
|
import { splitByNewLine } from '../../../helpers/helpers';
|
2019-06-03 12:41:45 +00:00
|
|
|
|
import Card from '../../ui/Card';
|
|
|
|
|
import Modal from './Modal';
|
2020-01-24 15:59:38 +00:00
|
|
|
|
import CellWrap from '../../ui/CellWrap';
|
2020-07-13 13:06:56 +00:00
|
|
|
|
import LogsSearchLink from '../../ui/LogsSearchLink';
|
2019-09-26 09:17:58 +00:00
|
|
|
|
|
2019-06-03 12:41:45 +00:00
|
|
|
|
class ClientsTable extends Component {
|
|
|
|
|
handleFormAdd = (values) => {
|
|
|
|
|
this.props.addClient(values);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
handleFormUpdate = (values, name) => {
|
|
|
|
|
this.props.updateClient(values, name);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
handleSubmit = (values) => {
|
2019-12-04 16:08:14 +00:00
|
|
|
|
const config = values;
|
|
|
|
|
|
|
|
|
|
if (values) {
|
|
|
|
|
if (values.blocked_services) {
|
|
|
|
|
config.blocked_services = Object
|
|
|
|
|
.keys(values.blocked_services)
|
2020-05-22 14:06:05 +00:00
|
|
|
|
.filter((service) => values.blocked_services[service]);
|
2019-12-04 16:08:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (values.upstreams && typeof values.upstreams === 'string') {
|
2020-07-15 09:35:37 +00:00
|
|
|
|
config.upstreams = splitByNewLine(values.upstreams);
|
2019-12-04 16:08:14 +00:00
|
|
|
|
} else {
|
|
|
|
|
config.upstreams = [];
|
|
|
|
|
}
|
2020-01-28 11:07:47 +00:00
|
|
|
|
|
|
|
|
|
if (values.tags) {
|
2020-05-22 14:06:05 +00:00
|
|
|
|
config.tags = values.tags.map((tag) => tag.value);
|
2020-02-13 09:02:29 +00:00
|
|
|
|
} else {
|
|
|
|
|
config.tags = [];
|
2020-01-28 11:07:47 +00:00
|
|
|
|
}
|
2019-07-18 11:52:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-07-06 16:58:44 +00:00
|
|
|
|
if (this.props.modalType === MODAL_TYPE.EDIT_FILTERS) {
|
2019-07-18 11:52:47 +00:00
|
|
|
|
this.handleFormUpdate(config, this.props.modalClientName);
|
2019-06-03 12:41:45 +00:00
|
|
|
|
} else {
|
2019-07-18 11:52:47 +00:00
|
|
|
|
this.handleFormAdd(config);
|
2019-06-03 12:41:45 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2020-05-22 14:06:05 +00:00
|
|
|
|
getOptionsWithLabels = (options) => (
|
2020-07-13 13:06:56 +00:00
|
|
|
|
options.map((option) => ({
|
|
|
|
|
value: option,
|
|
|
|
|
label: option,
|
|
|
|
|
}))
|
2020-01-28 11:07:47 +00:00
|
|
|
|
);
|
|
|
|
|
|
2019-06-03 12:41:45 +00:00
|
|
|
|
getClient = (name, clients) => {
|
2020-05-22 14:06:05 +00:00
|
|
|
|
const client = clients.find((item) => name === item.name);
|
2019-06-03 12:41:45 +00:00
|
|
|
|
|
|
|
|
|
if (client) {
|
2020-01-28 11:07:47 +00:00
|
|
|
|
const {
|
|
|
|
|
upstreams, tags, whois_info, ...values
|
|
|
|
|
} = client;
|
2019-06-03 12:41:45 +00:00
|
|
|
|
return {
|
2019-12-04 16:08:14 +00:00
|
|
|
|
upstreams: (upstreams && upstreams.join('\n')) || '',
|
2020-01-28 11:07:47 +00:00
|
|
|
|
tags: (tags && this.getOptionsWithLabels(tags)) || [],
|
2019-12-04 16:08:14 +00:00
|
|
|
|
...values,
|
2019-06-03 12:41:45 +00:00
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
2019-11-28 11:47:06 +00:00
|
|
|
|
ids: [''],
|
2020-01-28 11:07:47 +00:00
|
|
|
|
tags: [],
|
2019-06-03 12:41:45 +00:00
|
|
|
|
use_global_settings: true,
|
2019-07-18 11:52:47 +00:00
|
|
|
|
use_global_blocked_services: true,
|
2019-06-03 12:41:45 +00:00
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
handleDelete = (data) => {
|
|
|
|
|
// eslint-disable-next-line no-alert
|
|
|
|
|
if (window.confirm(this.props.t('client_confirm_delete', { key: data.name }))) {
|
|
|
|
|
this.props.deleteClient(data);
|
2020-01-13 14:41:59 +00:00
|
|
|
|
this.props.getStats();
|
2019-06-03 12:41:45 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
columns = [
|
|
|
|
|
{
|
|
|
|
|
Header: this.props.t('table_client'),
|
2019-11-28 11:47:06 +00:00
|
|
|
|
accessor: 'ids',
|
2019-09-23 13:01:51 +00:00
|
|
|
|
minWidth: 150,
|
2019-06-03 12:41:45 +00:00
|
|
|
|
Cell: (row) => {
|
2019-11-28 11:47:06 +00:00
|
|
|
|
const { value } = row;
|
2019-06-03 12:41:45 +00:00
|
|
|
|
|
2019-11-28 11:47:06 +00:00
|
|
|
|
return (
|
2020-06-17 21:36:19 +00:00
|
|
|
|
<div className="logs__row o-hidden">
|
2019-11-28 11:47:06 +00:00
|
|
|
|
<span className="logs__text">
|
2020-05-22 14:06:05 +00:00
|
|
|
|
{value.map((address) => (
|
2019-11-28 11:47:06 +00:00
|
|
|
|
<div key={address} title={address}>
|
|
|
|
|
{address}
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
2019-06-03 12:41:45 +00:00
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Header: this.props.t('table_name'),
|
|
|
|
|
accessor: 'name',
|
2019-09-23 13:01:51 +00:00
|
|
|
|
minWidth: 120,
|
2020-01-24 15:59:38 +00:00
|
|
|
|
Cell: CellWrap,
|
2019-06-03 12:41:45 +00:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Header: this.props.t('settings'),
|
|
|
|
|
accessor: 'use_global_settings',
|
2019-09-23 13:01:51 +00:00
|
|
|
|
minWidth: 120,
|
2019-06-03 12:41:45 +00:00
|
|
|
|
Cell: ({ value }) => {
|
|
|
|
|
const title = value ? (
|
|
|
|
|
<Trans>settings_global</Trans>
|
|
|
|
|
) : (
|
|
|
|
|
<Trans>settings_custom</Trans>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return (
|
2020-06-17 21:36:19 +00:00
|
|
|
|
<div className="logs__row o-hidden">
|
2019-11-28 11:47:06 +00:00
|
|
|
|
<div className="logs__text">{title}</div>
|
2019-06-03 12:41:45 +00:00
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
},
|
2019-07-18 11:52:47 +00:00
|
|
|
|
{
|
|
|
|
|
Header: this.props.t('blocked_services'),
|
|
|
|
|
accessor: 'blocked_services',
|
2019-09-23 13:02:47 +00:00
|
|
|
|
minWidth: 180,
|
2019-07-18 11:52:47 +00:00
|
|
|
|
Cell: (row) => {
|
|
|
|
|
const { value, original } = row;
|
|
|
|
|
|
|
|
|
|
if (original.use_global_blocked_services) {
|
|
|
|
|
return <Trans>settings_global</Trans>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="logs__row logs__row--icons">
|
2019-09-24 12:28:59 +00:00
|
|
|
|
{value && value.length > 0
|
2020-05-22 14:06:05 +00:00
|
|
|
|
? value.map((service) => (
|
2019-12-20 11:15:57 +00:00
|
|
|
|
<svg
|
|
|
|
|
className="service__icon service__icon--table"
|
|
|
|
|
title={service}
|
|
|
|
|
key={service}
|
|
|
|
|
>
|
|
|
|
|
<use xlinkHref={`#service_${service}`} />
|
|
|
|
|
</svg>
|
2019-09-24 12:28:59 +00:00
|
|
|
|
))
|
|
|
|
|
: '–'}
|
2019-07-18 11:52:47 +00:00
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
},
|
2019-12-04 16:08:14 +00:00
|
|
|
|
{
|
|
|
|
|
Header: this.props.t('upstreams'),
|
|
|
|
|
accessor: 'upstreams',
|
|
|
|
|
minWidth: 120,
|
|
|
|
|
Cell: ({ value }) => {
|
|
|
|
|
const title = value && value.length > 0 ? (
|
|
|
|
|
<Trans>settings_custom</Trans>
|
|
|
|
|
) : (
|
|
|
|
|
<Trans>settings_global</Trans>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return (
|
2020-06-17 21:36:19 +00:00
|
|
|
|
<div className="logs__row o-hidden">
|
2019-12-04 16:08:14 +00:00
|
|
|
|
<div className="logs__text">{title}</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
},
|
2020-01-28 11:07:47 +00:00
|
|
|
|
{
|
|
|
|
|
Header: this.props.t('tags_title'),
|
|
|
|
|
accessor: 'tags',
|
|
|
|
|
minWidth: 140,
|
|
|
|
|
Cell: (row) => {
|
|
|
|
|
const { value } = row;
|
|
|
|
|
|
|
|
|
|
if (!value || value.length < 1) {
|
|
|
|
|
return '–';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
2020-06-17 21:36:19 +00:00
|
|
|
|
<div className="logs__row o-hidden">
|
2020-01-28 11:07:47 +00:00
|
|
|
|
<span className="logs__text">
|
2020-05-22 14:06:05 +00:00
|
|
|
|
{value.map((tag) => (
|
2020-01-28 11:07:47 +00:00
|
|
|
|
<div key={tag} title={tag} className="small">
|
|
|
|
|
{tag}
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
},
|
2019-06-03 12:41:45 +00:00
|
|
|
|
{
|
2019-08-22 13:10:47 +00:00
|
|
|
|
Header: this.props.t('requests_count'),
|
2019-12-20 11:15:57 +00:00
|
|
|
|
id: 'statistics',
|
2020-05-22 14:06:05 +00:00
|
|
|
|
accessor: (row) => this.props.normalizedTopClients.configured[row.name] || 0,
|
2019-12-20 11:15:57 +00:00
|
|
|
|
sortMethod: (a, b) => b - a,
|
2019-09-23 13:02:47 +00:00
|
|
|
|
minWidth: 120,
|
2020-07-13 13:06:56 +00:00
|
|
|
|
Cell: (row) => {
|
|
|
|
|
const content = CellWrap(row);
|
|
|
|
|
|
|
|
|
|
if (!row.value) {
|
|
|
|
|
return content;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return <LogsSearchLink search={row.original.ids[0]}>{content}</LogsSearchLink>;
|
|
|
|
|
},
|
2019-06-03 12:41:45 +00:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Header: this.props.t('actions_table_header'),
|
|
|
|
|
accessor: 'actions',
|
2019-09-23 13:01:51 +00:00
|
|
|
|
maxWidth: 100,
|
2019-06-03 12:41:45 +00:00
|
|
|
|
Cell: (row) => {
|
|
|
|
|
const clientName = row.original.name;
|
|
|
|
|
const {
|
|
|
|
|
toggleClientModal, processingDeleting, processingUpdating, t,
|
|
|
|
|
} = this.props;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="logs__row logs__row--center">
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
className="btn btn-icon btn-outline-primary btn-sm mr-2"
|
2020-05-22 14:06:05 +00:00
|
|
|
|
onClick={() => toggleClientModal({
|
2020-07-06 16:58:44 +00:00
|
|
|
|
type: MODAL_TYPE.EDIT_FILTERS,
|
2020-05-22 14:06:05 +00:00
|
|
|
|
name: clientName,
|
|
|
|
|
})
|
2019-06-03 12:41:45 +00:00
|
|
|
|
}
|
|
|
|
|
disabled={processingUpdating}
|
|
|
|
|
title={t('edit_table_action')}
|
|
|
|
|
>
|
|
|
|
|
<svg className="icons">
|
|
|
|
|
<use xlinkHref="#edit" />
|
|
|
|
|
</svg>
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
className="btn btn-icon btn-outline-secondary btn-sm"
|
|
|
|
|
onClick={() => this.handleDelete({ name: clientName })}
|
|
|
|
|
disabled={processingDeleting}
|
|
|
|
|
title={t('delete_table_action')}
|
|
|
|
|
>
|
|
|
|
|
<svg className="icons">
|
|
|
|
|
<use xlinkHref="#delete" />
|
|
|
|
|
</svg>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
render() {
|
|
|
|
|
const {
|
|
|
|
|
t,
|
|
|
|
|
clients,
|
|
|
|
|
isModalOpen,
|
|
|
|
|
modalType,
|
|
|
|
|
modalClientName,
|
|
|
|
|
toggleClientModal,
|
|
|
|
|
processingAdding,
|
|
|
|
|
processingUpdating,
|
2020-01-28 11:07:47 +00:00
|
|
|
|
supportedTags,
|
2019-06-03 12:41:45 +00:00
|
|
|
|
} = this.props;
|
|
|
|
|
|
|
|
|
|
const currentClientData = this.getClient(modalClientName, clients);
|
2020-01-28 11:07:47 +00:00
|
|
|
|
const tagsOptions = this.getOptionsWithLabels(supportedTags);
|
2019-06-03 12:41:45 +00:00
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Card
|
|
|
|
|
title={t('clients_title')}
|
|
|
|
|
subtitle={t('clients_desc')}
|
|
|
|
|
bodyType="card-body box-body--settings"
|
|
|
|
|
>
|
|
|
|
|
<Fragment>
|
|
|
|
|
<ReactTable
|
|
|
|
|
data={clients || []}
|
|
|
|
|
columns={this.columns}
|
2019-12-20 11:15:57 +00:00
|
|
|
|
defaultSorted={[
|
|
|
|
|
{
|
|
|
|
|
id: 'statistics',
|
|
|
|
|
asc: true,
|
|
|
|
|
},
|
|
|
|
|
]}
|
2019-06-03 12:41:45 +00:00
|
|
|
|
className="-striped -highlight card-table-overflow"
|
2020-06-17 21:36:19 +00:00
|
|
|
|
showPagination
|
2019-06-03 12:41:45 +00:00
|
|
|
|
defaultPageSize={10}
|
|
|
|
|
minRows={5}
|
2020-07-24 13:45:59 +00:00
|
|
|
|
ofText="/"
|
|
|
|
|
previousText={t('previous_btn')}
|
|
|
|
|
nextText={t('next_btn')}
|
|
|
|
|
pageText={t('page_table_footer_text')}
|
2019-06-03 12:41:45 +00:00
|
|
|
|
rowsText={t('rows_table_footer_text')}
|
2020-07-24 13:45:59 +00:00
|
|
|
|
loadingText={t('loading_table_status')}
|
2019-06-03 12:41:45 +00:00
|
|
|
|
noDataText={t('clients_not_found')}
|
|
|
|
|
/>
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
className="btn btn-success btn-standard mt-3"
|
2020-07-06 16:58:44 +00:00
|
|
|
|
onClick={() => toggleClientModal(MODAL_TYPE.ADD_FILTERS)}
|
2019-06-03 12:41:45 +00:00
|
|
|
|
disabled={processingAdding}
|
|
|
|
|
>
|
|
|
|
|
<Trans>client_add</Trans>
|
|
|
|
|
</button>
|
|
|
|
|
<Modal
|
|
|
|
|
isModalOpen={isModalOpen}
|
|
|
|
|
modalType={modalType}
|
|
|
|
|
toggleClientModal={toggleClientModal}
|
|
|
|
|
currentClientData={currentClientData}
|
|
|
|
|
handleSubmit={this.handleSubmit}
|
|
|
|
|
processingAdding={processingAdding}
|
|
|
|
|
processingUpdating={processingUpdating}
|
2020-01-28 11:07:47 +00:00
|
|
|
|
tagsOptions={tagsOptions}
|
2019-06-03 12:41:45 +00:00
|
|
|
|
/>
|
|
|
|
|
</Fragment>
|
|
|
|
|
</Card>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ClientsTable.propTypes = {
|
|
|
|
|
t: PropTypes.func.isRequired,
|
|
|
|
|
clients: PropTypes.array.isRequired,
|
2019-12-20 11:15:57 +00:00
|
|
|
|
normalizedTopClients: PropTypes.object.isRequired,
|
2019-06-03 12:41:45 +00:00
|
|
|
|
toggleClientModal: PropTypes.func.isRequired,
|
|
|
|
|
deleteClient: PropTypes.func.isRequired,
|
|
|
|
|
addClient: PropTypes.func.isRequired,
|
|
|
|
|
updateClient: PropTypes.func.isRequired,
|
|
|
|
|
isModalOpen: PropTypes.bool.isRequired,
|
|
|
|
|
modalType: PropTypes.string.isRequired,
|
|
|
|
|
modalClientName: PropTypes.string.isRequired,
|
|
|
|
|
processingAdding: PropTypes.bool.isRequired,
|
|
|
|
|
processingDeleting: PropTypes.bool.isRequired,
|
|
|
|
|
processingUpdating: PropTypes.bool.isRequired,
|
2020-01-13 14:41:59 +00:00
|
|
|
|
getStats: PropTypes.func.isRequired,
|
2020-01-28 11:07:47 +00:00
|
|
|
|
supportedTags: PropTypes.array.isRequired,
|
2019-06-03 12:41:45 +00:00
|
|
|
|
};
|
|
|
|
|
|
2020-05-22 14:06:05 +00:00
|
|
|
|
export default withTranslation()(ClientsTable);
|