+ client: handle client tags
This commit is contained in:
committed by
Simon Zolin
parent
b519c3a83f
commit
67956597be
@@ -327,7 +327,7 @@
|
||||
"client_edit": "Edit Client",
|
||||
"client_identifier": "Identifier",
|
||||
"ip_address": "IP address",
|
||||
"client_identifier_desc": "Clients can be identified by the IP address, CIDR, MAC address. Please note, that using MAC as identifier is possible only if AdGuard Home is also a <0>DHCP server</0>",
|
||||
"client_identifier_desc": "Clients can be identified by the IP address, CIDR, MAC address. Please note that using MAC as identifier is possible only if AdGuard Home is also a <0>DHCP server</0>",
|
||||
"form_enter_ip": "Enter IP",
|
||||
"form_enter_mac": "Enter MAC",
|
||||
"form_enter_id": "Enter identifier",
|
||||
@@ -443,5 +443,8 @@
|
||||
"disable_ipv6_desc": "If this feature is enabled, all DNS queries for IPv6 addresses (type AAAA) will be dropped.",
|
||||
"autofix_warning_text": "If you click \"Fix\", AdGuardHome will configure your system to use AdGuardHome DNS server.",
|
||||
"autofix_warning_list": "It will perform these tasks: <0>Deactivate system DNSStubListener</0> <0>Set DNS server address to 127.0.0.1</0> <0>Replace symbolic link target of /etc/resolv.conf to /run/systemd/resolve/resolv.conf</0> <0>Stop DNSStubListener (reload systemd-resolved service)</0>",
|
||||
"autofix_warning_result": "As a result all DNS requests from your system will be processed by AdGuardHome by default."
|
||||
"autofix_warning_result": "As a result all DNS requests from your system will be processed by AdGuardHome by default.",
|
||||
"tags_title": "Tags",
|
||||
"tags_desc": "You can select the tags that correspond to the client. Tags can be included in the filtering rules and allow you to apply them more accurately. <0>Learn more</0>",
|
||||
"form_select_tags": "Select client tags"
|
||||
}
|
||||
|
||||
@@ -208,6 +208,7 @@ export const getClients = () => async (dispatch) => {
|
||||
dispatch(getClientsSuccess({
|
||||
clients: sortedClients || [],
|
||||
autoClients: sortedAutoClients || [],
|
||||
supportedTags: data.supported_tags || [],
|
||||
}));
|
||||
} catch (error) {
|
||||
dispatch(addErrorToast({ error }));
|
||||
|
||||
@@ -33,6 +33,10 @@ class ClientsTable extends Component {
|
||||
} else {
|
||||
config.upstreams = [];
|
||||
}
|
||||
|
||||
if (values.tags) {
|
||||
config.tags = values.tags.map(tag => tag.value);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.props.modalType === MODAL_TYPE.EDIT) {
|
||||
@@ -40,22 +44,29 @@ class ClientsTable extends Component {
|
||||
} else {
|
||||
this.handleFormAdd(config);
|
||||
}
|
||||
this.props.getStats();
|
||||
};
|
||||
|
||||
getOptionsWithLabels = options => (
|
||||
options.map(option => ({ value: option, label: option }))
|
||||
);
|
||||
|
||||
getClient = (name, clients) => {
|
||||
const client = clients.find(item => name === item.name);
|
||||
|
||||
if (client) {
|
||||
const { upstreams, whois_info, ...values } = client;
|
||||
const {
|
||||
upstreams, tags, whois_info, ...values
|
||||
} = client;
|
||||
return {
|
||||
upstreams: (upstreams && upstreams.join('\n')) || '',
|
||||
tags: (tags && this.getOptionsWithLabels(tags)) || [],
|
||||
...values,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
ids: [''],
|
||||
tags: [],
|
||||
use_global_settings: true,
|
||||
use_global_blocked_services: true,
|
||||
};
|
||||
@@ -160,6 +171,30 @@ class ClientsTable extends Component {
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
Header: this.props.t('tags_title'),
|
||||
accessor: 'tags',
|
||||
minWidth: 140,
|
||||
Cell: (row) => {
|
||||
const { value } = row;
|
||||
|
||||
if (!value || value.length < 1) {
|
||||
return '–';
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="logs__row logs__row--overflow">
|
||||
<span className="logs__text">
|
||||
{value.map(tag => (
|
||||
<div key={tag} title={tag} className="small">
|
||||
{tag}
|
||||
</div>
|
||||
))}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
Header: this.props.t('requests_count'),
|
||||
id: 'statistics',
|
||||
@@ -223,9 +258,11 @@ class ClientsTable extends Component {
|
||||
toggleClientModal,
|
||||
processingAdding,
|
||||
processingUpdating,
|
||||
supportedTags,
|
||||
} = this.props;
|
||||
|
||||
const currentClientData = this.getClient(modalClientName, clients);
|
||||
const tagsOptions = this.getOptionsWithLabels(supportedTags);
|
||||
|
||||
return (
|
||||
<Card
|
||||
@@ -272,6 +309,7 @@ class ClientsTable extends Component {
|
||||
handleSubmit={this.handleSubmit}
|
||||
processingAdding={processingAdding}
|
||||
processingUpdating={processingUpdating}
|
||||
tagsOptions={tagsOptions}
|
||||
/>
|
||||
</Fragment>
|
||||
</Card>
|
||||
@@ -294,6 +332,7 @@ ClientsTable.propTypes = {
|
||||
processingDeleting: PropTypes.bool.isRequired,
|
||||
processingUpdating: PropTypes.bool.isRequired,
|
||||
getStats: PropTypes.func.isRequired,
|
||||
supportedTags: PropTypes.array.isRequired,
|
||||
};
|
||||
|
||||
export default withNamespaces()(ClientsTable);
|
||||
|
||||
@@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
|
||||
import { Field, FieldArray, reduxForm, formValueSelector } from 'redux-form';
|
||||
import { Trans, withNamespaces } from 'react-i18next';
|
||||
import flow from 'lodash/flow';
|
||||
import Select from 'react-select';
|
||||
|
||||
import i18n from '../../../i18n';
|
||||
import Tabs from '../../ui/Tabs';
|
||||
@@ -99,6 +100,23 @@ const renderFieldsWrapper = (placeholder, buttonTitle) =>
|
||||
// Should create function outside of component to prevent component re-renders
|
||||
const renderFields = renderFieldsWrapper(i18n.t('form_enter_id'), i18n.t('form_add_id'));
|
||||
|
||||
const renderMultiselect = (props) => {
|
||||
const { input, placeholder, options } = props;
|
||||
|
||||
return (
|
||||
<Select
|
||||
{...input}
|
||||
options={options}
|
||||
className="basic-multi-select"
|
||||
classNamePrefix="select"
|
||||
onChange={value => input.onChange(value)}
|
||||
onBlur={() => input.onBlur(input.value)}
|
||||
placeholder={placeholder}
|
||||
isMulti
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
let Form = (props) => {
|
||||
const {
|
||||
t,
|
||||
@@ -113,6 +131,7 @@ let Form = (props) => {
|
||||
processingAdding,
|
||||
processingUpdating,
|
||||
invalid,
|
||||
tagsOptions,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
@@ -131,6 +150,27 @@ let Form = (props) => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form__group mb-4">
|
||||
<div className="form__label">
|
||||
<strong className="mr-3">
|
||||
<Trans>tags_title</Trans>
|
||||
</strong>
|
||||
</div>
|
||||
<div className="form__desc mt-0 mb-2">
|
||||
<Trans components={[
|
||||
<a href="https://github.com/AdguardTeam/AdGuardHome/wiki/Hosts-Blocklists#ctag" key="0">link</a>,
|
||||
]}>
|
||||
tags_desc
|
||||
</Trans>
|
||||
</div>
|
||||
<Field
|
||||
name="tags"
|
||||
component={renderMultiselect}
|
||||
placeholder={t('form_select_tags')}
|
||||
options={tagsOptions}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form__group">
|
||||
<div className="form__label">
|
||||
<strong className="mr-3">
|
||||
@@ -286,6 +326,7 @@ Form.propTypes = {
|
||||
processingAdding: PropTypes.bool.isRequired,
|
||||
processingUpdating: PropTypes.bool.isRequired,
|
||||
invalid: PropTypes.bool.isRequired,
|
||||
tagsOptions: PropTypes.array.isRequired,
|
||||
};
|
||||
|
||||
const selector = formValueSelector('clientForm');
|
||||
|
||||
@@ -33,6 +33,7 @@ const Modal = (props) => {
|
||||
toggleClientModal,
|
||||
processingAdding,
|
||||
processingUpdating,
|
||||
tagsOptions,
|
||||
} = props;
|
||||
const initialData = getInitialData(currentClientData);
|
||||
|
||||
@@ -62,6 +63,7 @@ const Modal = (props) => {
|
||||
toggleClientModal={toggleClientModal}
|
||||
processingAdding={processingAdding}
|
||||
processingUpdating={processingUpdating}
|
||||
tagsOptions={tagsOptions}
|
||||
/>
|
||||
</div>
|
||||
</ReactModal>
|
||||
@@ -76,6 +78,7 @@ Modal.propTypes = {
|
||||
toggleClientModal: PropTypes.func.isRequired,
|
||||
processingAdding: PropTypes.bool.isRequired,
|
||||
processingUpdating: PropTypes.bool.isRequired,
|
||||
tagsOptions: PropTypes.array.isRequired,
|
||||
};
|
||||
|
||||
export default withNamespaces()(Modal);
|
||||
|
||||
@@ -46,6 +46,7 @@ class Clients extends Component {
|
||||
processingDeleting={clients.processingDeleting}
|
||||
processingUpdating={clients.processingUpdating}
|
||||
getStats={getStats}
|
||||
supportedTags={dashboard.supportedTags}
|
||||
/>
|
||||
<AutoClients
|
||||
autoClients={dashboard.autoClients}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
height: 45px;
|
||||
padding: 0 32px 2px 33px;
|
||||
outline: 0;
|
||||
border-color: rgba(0, 40, 100, 0.12);
|
||||
border-color: rgba(69, 79, 94, 0.12);
|
||||
background-image: url("./svg/globe.svg"), url("./svg/chevron-down.svg");
|
||||
background-repeat: no-repeat, no-repeat;
|
||||
background-position: left 11px center, right 9px center;
|
||||
@@ -14,3 +14,26 @@
|
||||
.select--language::-ms-expand {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.basic-multi-select .select__control {
|
||||
border: 1px solid rgba(0, 40, 100, 0.12);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.basic-multi-select .select__control:hover {
|
||||
border: 1px solid rgba(0, 40, 100, 0.12);
|
||||
}
|
||||
|
||||
.basic-multi-select .select__control--is-focused,
|
||||
.basic-multi-select .select__control--is-focused:hover {
|
||||
border-color: #1991eb;
|
||||
box-shadow: 0 0 0 2px rgba(70, 127, 207, 0.25);
|
||||
}
|
||||
|
||||
.basic-multi-select .select__placeholder {
|
||||
color: #adb5bd;
|
||||
}
|
||||
|
||||
.basic-multi-select .select__menu {
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
@@ -153,8 +153,7 @@ const dashboard = handleActions(
|
||||
[actions.getClientsSuccess]: (state, { payload }) => {
|
||||
const newState = {
|
||||
...state,
|
||||
clients: payload.clients,
|
||||
autoClients: payload.autoClients,
|
||||
...payload,
|
||||
processingClients: false,
|
||||
};
|
||||
return newState;
|
||||
@@ -205,6 +204,7 @@ const dashboard = handleActions(
|
||||
dnsVersion: '',
|
||||
clients: [],
|
||||
autoClients: [],
|
||||
supportedTags: [],
|
||||
name: '',
|
||||
},
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user