Files
AdGuardHome/client/src/components/Settings/Clients/Form.js
Artem Baskal 6f3cd4e7eb + client: Update packages - Merge pull request #613 in DNS/adguard-home from fix/1597 to master
Close #1597

Squashed commit of the following:

commit 1eb89586dd71260e561420fe669abc8b56a506a1
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Wed May 20 14:54:10 2020 +0300

    Fix translation in install options

commit 1ebdc9ebfe12a609f978e47db6505c7095b10f7e
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Wed May 20 13:11:34 2020 +0300

    Remove commented code

commit 2a8302c65a2a3cf7b6b1596115d1153dac32a794
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Tue May 19 19:02:49 2020 +0300

    Update i18n packages, add development browserlist, downgrade eslint to match peerDepencancies version

commit 3fcf73fb14cd9da508522d1a300b66af24da95e5
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Tue May 19 17:30:37 2020 +0300

    Remove all unused dependencies

commit e761810e3e54e188ada41245bdce7414cd0f03e8
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Tue May 19 16:35:24 2020 +0300

    Remove unused dependencies

commit d89d27da6befcaabcdc12bf5e7e94cbb24140010
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Tue May 19 16:14:09 2020 +0300

    Update regular dependencies

commit d2dfd01233d059870d5173ffd748cf61a477936f
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Tue May 19 15:34:10 2020 +0300

    Update all dev dependancies

commit 02b6fb480e9d310039fbe9b7aae062a41128f070
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Tue May 19 14:14:44 2020 +0300

    Update all postcss packages

commit 5e1fa5f99ad75f77e5e429b28ee1ca0b5e65a9a0
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Tue May 19 12:45:02 2020 +0300

    Prevent git from converting linebreaks in .js files

commit 0b9b3b0dccd47cfa50c9531fb61729e6b5a04523
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Tue May 19 12:43:01 2020 +0300

    Prevent git from converting linebreaks in .js files

commit 18b7495e9ef7130b1ac4dbba84c54127d16c6350
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Tue May 19 12:24:47 2020 +0300

    Remove linebreak-style eslint rule

commit df893dec53adebb1d662fe805fab508fd4ed5e06
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon May 18 20:55:47 2020 +0300

    Add prop types

commit 36178ecfc5c7fa11a6ee08d7705ca8560941af40
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon May 18 18:52:07 2020 +0300

    Update eslint and babel, fix eslint warnings

commit f045b4a2e6b9b78f7e88e3b5d1419c29966a8230
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon May 18 16:45:49 2020 +0300

    Update css loading webpack rules

commit 247fa1ed548ef0706a03fdada8309c1454d191f8
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Sat May 16 16:13:49 2020 +0300

    Suppress linebreak-style eslint error for Windows

commit d6499aac507100d6918c849c06d739d80f2229f0
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Sat May 16 14:55:07 2020 +0300

    Suppress eslint exit code

commit ae2d6c614ea23a90d515168f8752e959298894ef
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Sat May 16 14:05:18 2020 +0300

    Edit css file warnings

commit 60675050f2a5baebc679fc05da7e033e5c740d90
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Sat May 16 13:10:26 2020 +0300

    Remove uglifyjs plugin

commit a27806434dd8672e71a26c7a2e810d77e5e229fa
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Sat May 16 01:29:17 2020 +0300

    Fix DefinePlugin value

commit 8f2966ca59195c2f70bca5072d20515d536f42a6
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Sat May 16 01:05:03 2020 +0300

    Update webpack
2020-05-22 17:06:05 +03:00

360 lines
13 KiB
JavaScript

import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import {
Field, FieldArray, reduxForm, formValueSelector,
} from 'redux-form';
import { Trans, withTranslation } from 'react-i18next';
import flow from 'lodash/flow';
import Select from 'react-select';
import i18n from '../../../i18n';
import Tabs from '../../ui/Tabs';
import Examples from '../Dns/Upstream/Examples';
import { toggleAllServices } from '../../../helpers/helpers';
import {
required,
clientId,
renderInputField,
renderGroupField,
renderSelectField,
renderServiceField,
} from '../../../helpers/form';
import { SERVICES } from '../../../helpers/constants';
import './Service.css';
const settingsCheckboxes = [
{
name: 'use_global_settings',
placeholder: 'client_global_settings',
},
{
name: 'filtering_enabled',
placeholder: 'block_domain_use_filters_and_hosts',
},
{
name: 'safebrowsing_enabled',
placeholder: 'use_adguard_browsing_sec',
},
{
name: 'parental_enabled',
placeholder: 'use_adguard_parental',
},
{
name: 'safesearch_enabled',
placeholder: 'enforce_safe_search',
},
];
const validate = (values) => {
const errors = {};
const { name, ids } = values;
errors.name = required(name);
if (ids && ids.length) {
const idArrayErrors = [];
ids.forEach((id, idx) => {
idArrayErrors[idx] = required(id) || clientId(id);
});
if (idArrayErrors.length) {
errors.ids = idArrayErrors;
}
}
return errors;
};
const renderFieldsWrapper = (placeholder, buttonTitle) => function cell(row) {
const {
fields,
} = row;
return (
<div className="form__group">
{fields.map((ip, index) => (
<div key={index} className="mb-1">
<Field
name={ip}
component={renderGroupField}
type="text"
className="form-control"
placeholder={placeholder}
isActionAvailable={index !== 0}
removeField={() => fields.remove(index)}
normalizeOnBlur={(data) => data.trim()}
/>
</div>
))}
<button
type="button"
className="btn btn-link btn-block btn-sm"
onClick={() => fields.push()}
title={buttonTitle}
>
<svg className="icon icon--close">
<use xlinkHref="#plus" />
</svg>
</button>
</div>
);
};
// 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}
blurInputOnSelect={false}
isMulti
/>
);
};
renderMultiselect.propTypes = {
input: PropTypes.object.isRequired,
placeholder: PropTypes.string,
options: PropTypes.object,
};
let Form = (props) => {
const {
t,
handleSubmit,
reset,
change,
pristine,
submitting,
useGlobalSettings,
useGlobalServices,
toggleClientModal,
processingAdding,
processingUpdating,
invalid,
tagsOptions,
} = props;
return (
<form onSubmit={handleSubmit}>
<div className="modal-body">
<div className="form__group mb-0">
<div className="form__group">
<Field
id="name"
name="name"
component={renderInputField}
type="text"
className="form-control"
placeholder={t('form_client_name')}
normalizeOnBlur={(data) => data.trim()}
/>
</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">
<Trans>client_identifier</Trans>
</strong>
</div>
<div className="form__desc mt-0">
<Trans
components={[
<a href="#dhcp" key="0">
link
</a>,
]}
>
client_identifier_desc
</Trans>
</div>
</div>
<div className="form__group">
<FieldArray
name="ids"
component={renderFields}
/>
</div>
</div>
<Tabs controlClass="form">
<div label="settings" title={props.t('main_settings')}>
{settingsCheckboxes.map((setting) => (
<div className="form__group" key={setting.name}>
<Field
name={setting.name}
type="checkbox"
component={renderSelectField}
placeholder={t(setting.placeholder)}
disabled={
setting.name !== 'use_global_settings'
? useGlobalSettings
: false
}
/>
</div>
))}
</div>
<div label="services" title={props.t('block_services')}>
<div className="form__group">
<Field
name="use_global_blocked_services"
type="checkbox"
component={renderServiceField}
placeholder={t('blocked_services_global')}
modifier="service--global"
/>
<div className="row mb-4">
<div className="col-6">
<button
type="button"
className="btn btn-secondary btn-block"
disabled={useGlobalServices}
onClick={() => toggleAllServices(SERVICES, change, true)}
>
<Trans>block_all</Trans>
</button>
</div>
<div className="col-6">
<button
type="button"
className="btn btn-secondary btn-block"
disabled={useGlobalServices}
onClick={() => toggleAllServices(SERVICES, change, false)}
>
<Trans>unblock_all</Trans>
</button>
</div>
</div>
<div className="services">
{SERVICES.map((service) => (
<Field
key={service.id}
icon={`service_${service.id}`}
name={`blocked_services.${service.id}`}
type="checkbox"
component={renderServiceField}
placeholder={service.name}
disabled={useGlobalServices}
/>
))}
</div>
</div>
</div>
<div label="upstream" title={props.t('upstream_dns')}>
<div className="form__desc mb-3">
<Trans components={[<a href="#dns" key="0">link</a>]}>
upstream_dns_client_desc
</Trans>
</div>
<Field
id="upstreams"
name="upstreams"
component="textarea"
type="text"
className="form-control form-control--textarea mb-5"
placeholder={t('upstream_dns')}
/>
<Examples />
</div>
</Tabs>
</div>
<div className="modal-footer">
<div className="btn-list">
<button
type="button"
className="btn btn-secondary btn-standard"
disabled={submitting}
onClick={() => {
reset();
toggleClientModal();
}}
>
<Trans>cancel_btn</Trans>
</button>
<button
type="submit"
className="btn btn-success btn-standard"
disabled={
submitting
|| invalid
|| pristine
|| processingAdding
|| processingUpdating
}
>
<Trans>save_btn</Trans>
</button>
</div>
</div>
</form>
);
};
Form.propTypes = {
pristine: PropTypes.bool.isRequired,
handleSubmit: PropTypes.func.isRequired,
reset: PropTypes.func.isRequired,
change: PropTypes.func.isRequired,
submitting: PropTypes.bool.isRequired,
toggleClientModal: PropTypes.func.isRequired,
useGlobalSettings: PropTypes.bool,
useGlobalServices: PropTypes.bool,
t: PropTypes.func.isRequired,
processingAdding: PropTypes.bool.isRequired,
processingUpdating: PropTypes.bool.isRequired,
invalid: PropTypes.bool.isRequired,
tagsOptions: PropTypes.array.isRequired,
};
const selector = formValueSelector('clientForm');
Form = connect((state) => {
const useGlobalSettings = selector(state, 'use_global_settings');
const useGlobalServices = selector(state, 'use_global_blocked_services');
return {
useGlobalSettings,
useGlobalServices,
};
})(Form);
export default flow([
withTranslation(),
reduxForm({
form: 'clientForm',
enableReinitialize: true,
validate,
}),
])(Form);