+ client: split settings page into several pages

This commit is contained in:
Ildar Kamalov
2019-06-03 15:41:45 +03:00
parent 1d09ff0562
commit cf53653cfa
22 changed files with 882 additions and 492 deletions

View File

@@ -0,0 +1,260 @@
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import { Trans, withNamespaces } from 'react-i18next';
import ReactTable from 'react-table';
import { MODAL_TYPE, CLIENT_ID } from '../../../helpers/constants';
import Card from '../../ui/Card';
import Modal from './Modal';
class ClientsTable extends Component {
handleFormAdd = (values) => {
this.props.addClient(values);
};
handleFormUpdate = (values, name) => {
this.props.updateClient(values, name);
};
handleSubmit = (values) => {
if (this.props.modalType === MODAL_TYPE.EDIT) {
this.handleFormUpdate(values, this.props.modalClientName);
} else {
this.handleFormAdd(values);
}
};
cellWrap = ({ value }) => (
<div className="logs__row logs__row--overflow">
<span className="logs__text" title={value}>
{value}
</span>
</div>
);
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: CLIENT_ID.IP,
use_global_settings: true,
};
};
getStats = (ip, stats) => {
if (stats && stats.top_clients) {
return stats.top_clients[ip];
}
return '';
};
handleDelete = (data) => {
// eslint-disable-next-line no-alert
if (window.confirm(this.props.t('client_confirm_delete', { key: data.name }))) {
this.props.deleteClient(data);
}
};
columns = [
{
Header: this.props.t('table_client'),
accessor: 'ip',
Cell: (row) => {
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>
);
} else 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>
);
}
return '';
},
},
{
Header: this.props.t('table_name'),
accessor: 'name',
Cell: this.cellWrap,
},
{
Header: this.props.t('settings'),
accessor: 'use_global_settings',
Cell: ({ value }) => {
const title = value ? (
<Trans>settings_global</Trans>
) : (
<Trans>settings_custom</Trans>
);
return (
<div className="logs__row logs__row--overflow">
<div className="logs__text" title={title}>
{title}
</div>
</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 '';
},
},
{
Header: this.props.t('actions_table_header'),
accessor: 'actions',
maxWidth: 150,
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"
onClick={() =>
toggleClientModal({
type: MODAL_TYPE.EDIT,
name: clientName,
})
}
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,
} = this.props;
const currentClientData = this.getClient(modalClientName, clients);
return (
<Card
title={t('clients_title')}
subtitle={t('clients_desc')}
bodyType="card-body box-body--settings"
>
<Fragment>
<ReactTable
data={clients || []}
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')}
/>
<button
type="button"
className="btn btn-success btn-standard mt-3"
onClick={() => toggleClientModal(MODAL_TYPE.ADD)}
disabled={processingAdding}
>
<Trans>client_add</Trans>
</button>
<Modal
isModalOpen={isModalOpen}
modalType={modalType}
toggleClientModal={toggleClientModal}
currentClientData={currentClientData}
handleSubmit={this.handleSubmit}
processingAdding={processingAdding}
processingUpdating={processingUpdating}
/>
</Fragment>
</Card>
);
}
}
ClientsTable.propTypes = {
t: PropTypes.func.isRequired,
clients: PropTypes.array.isRequired,
topStats: PropTypes.object.isRequired,
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,
};
export default withNamespaces()(ClientsTable);

View File

@@ -1,251 +1,50 @@
import React, { Component, Fragment } from 'react';
import { withNamespaces } from 'react-i18next';
import PropTypes from 'prop-types';
import { Trans, withNamespaces } from 'react-i18next';
import ReactTable from 'react-table';
import { MODAL_TYPE, CLIENT_ID } from '../../../helpers/constants';
import Card from '../../ui/Card';
import Modal from './Modal';
import ClientsTable from './ClientsTable';
import AutoClients from './AutoClients';
import PageTitle from '../../ui/PageTitle';
import Loading from '../../ui/Loading';
class Clients extends Component {
handleFormAdd = (values) => {
this.props.addClient(values);
};
handleFormUpdate = (values, name) => {
this.props.updateClient(values, name);
};
handleSubmit = (values) => {
if (this.props.modalType === MODAL_TYPE.EDIT) {
this.handleFormUpdate(values, this.props.modalClientName);
} else {
this.handleFormAdd(values);
}
};
cellWrap = ({ value }) => (
<div className="logs__row logs__row--overflow">
<span className="logs__text" title={value}>
{value}
</span>
</div>
);
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: CLIENT_ID.IP,
use_global_settings: true,
};
};
getStats = (ip, stats) => {
if (stats && stats.top_clients) {
return stats.top_clients[ip];
}
return '';
};
handleDelete = (data) => {
// eslint-disable-next-line no-alert
if (window.confirm(this.props.t('client_confirm_delete', { key: data.name }))) {
this.props.deleteClient(data);
}
}
columns = [
{
Header: this.props.t('table_client'),
accessor: 'ip',
Cell: (row) => {
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>
);
} else 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>
);
}
return '';
},
},
{
Header: this.props.t('table_name'),
accessor: 'name',
Cell: this.cellWrap,
},
{
Header: this.props.t('settings'),
accessor: 'use_global_settings',
Cell: ({ value }) => {
const title = value ? (
<Trans>settings_global</Trans>
) : (
<Trans>settings_custom</Trans>
);
return (
<div className="logs__row logs__row--overflow">
<div className="logs__text" title={title}>
{title}
</div>
</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 '';
},
},
{
Header: this.props.t('actions_table_header'),
accessor: 'actions',
maxWidth: 150,
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"
onClick={() =>
toggleClientModal({
type: MODAL_TYPE.EDIT,
name: clientName,
})
}
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,
} = this.props;
const currentClientData = this.getClient(modalClientName, clients);
const { dashboard, clients, t } = this.props;
return (
<Card
title={t('clients_title')}
subtitle={t('clients_desc')}
bodyType="card-body box-body--settings"
>
<Fragment>
<ReactTable
data={clients || []}
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')}
/>
<button
type="button"
className="btn btn-success btn-standard mt-3"
onClick={() => toggleClientModal(MODAL_TYPE.ADD)}
disabled={processingAdding}
>
<Trans>client_add</Trans>
</button>
<Modal
isModalOpen={isModalOpen}
modalType={modalType}
toggleClientModal={toggleClientModal}
currentClientData={currentClientData}
handleSubmit={this.handleSubmit}
processingAdding={processingAdding}
processingUpdating={processingUpdating}
/>
</Fragment>
</Card>
<Fragment>
<PageTitle title={t('clients_settings')} />
{!dashboard.processingTopStats || (!dashboard.processingClients && <Loading />)}
{!dashboard.processingTopStats && !dashboard.processingClients && (
<Fragment>
<ClientsTable
clients={dashboard.clients}
topStats={dashboard.topStats}
isModalOpen={clients.isModalOpen}
modalClientName={clients.modalClientName}
modalType={clients.modalType}
addClient={this.props.addClient}
updateClient={this.props.updateClient}
deleteClient={this.props.deleteClient}
toggleClientModal={this.props.toggleClientModal}
processingAdding={clients.processingAdding}
processingDeleting={clients.processingDeleting}
processingUpdating={clients.processingUpdating}
/>
<AutoClients
autoClients={dashboard.autoClients}
topStats={dashboard.topStats}
/>
</Fragment>
)}
</Fragment>
);
}
}
Clients.propTypes = {
t: PropTypes.func.isRequired,
dashboard: PropTypes.object.isRequired,
clients: PropTypes.array.isRequired,
topStats: PropTypes.object.isRequired,
toggleClientModal: PropTypes.func.isRequired,

View File

@@ -9,8 +9,15 @@ import Leases from './Leases';
import StaticLeases from './StaticLeases/index';
import Card from '../../ui/Card';
import Accordion from '../../ui/Accordion';
import PageTitle from '../../ui/PageTitle';
import Loading from '../../ui/Loading';
class Dhcp extends Component {
componentDidMount() {
this.props.getDhcpStatus();
this.props.getDhcpInterfaces();
}
handleFormSubmit = (values) => {
if (values.interface_name) {
this.props.setDhcpConfig(values);
@@ -19,7 +26,7 @@ class Dhcp extends Component {
handleToggle = (config) => {
this.props.toggleDhcp(config);
}
};
getToggleDhcpButton = () => {
const {
@@ -54,17 +61,13 @@ class Dhcp extends Component {
className="btn btn-standard mr-2 btn-success"
onClick={() => this.handleToggle(config)}
disabled={
!filledConfig
|| !check
|| otherDhcpFound
|| processingDhcp
|| processingConfig
!filledConfig || !check || otherDhcpFound || processingDhcp || processingConfig
}
>
<Trans>dhcp_enable</Trans>
</button>
);
}
};
getActiveDhcpMessage = (t, check) => {
const { found } = check.otherServer;
@@ -95,7 +98,7 @@ class Dhcp extends Component {
)}
</div>
);
}
};
getDhcpWarning = (check) => {
if (check.otherServer.found === DHCP_STATUS_RESPONSE.NO) {
@@ -107,7 +110,7 @@ class Dhcp extends Component {
<Trans>dhcp_warning</Trans>
</div>
);
}
};
getStaticIpWarning = (t, check, interfaceName) => {
if (check.staticIP.static === DHCP_STATUS_RESPONSE.ERROR) {
@@ -121,21 +124,19 @@ class Dhcp extends Component {
</Accordion>
</div>
</div>
<hr className="mt-4 mb-4"/>
<hr className="mt-4 mb-4" />
</Fragment>
);
} else if (
check.staticIP.static === DHCP_STATUS_RESPONSE.NO
&& check.staticIP.ip
&& interfaceName
check.staticIP.static === DHCP_STATUS_RESPONSE.NO &&
check.staticIP.ip &&
interfaceName
) {
return (
<Fragment>
<div className="text-secondary mb-2">
<Trans
components={[
<strong key="0">example</strong>,
]}
components={[<strong key="0">example</strong>]}
values={{
interfaceName,
ipAddress: check.staticIP.ip,
@@ -144,13 +145,13 @@ class Dhcp extends Component {
dhcp_dynamic_ip_found
</Trans>
</div>
<hr className="mt-4 mb-4"/>
<hr className="mt-4 mb-4" />
</Fragment>
);
}
return '';
}
};
render() {
const { t, dhcp } = this.props;
@@ -158,93 +159,101 @@ class Dhcp extends Component {
'btn btn-primary btn-standard': true,
'btn btn-primary btn-standard btn-loading': dhcp.processingStatus,
});
const {
enabled,
interface_name,
...values
} = dhcp.config;
const { enabled, interface_name, ...values } = dhcp.config;
return (
<Fragment>
<Card title={ t('dhcp_title') } subtitle={ t('dhcp_description') } bodyType="card-body box-body--settings">
<div className="dhcp">
{!dhcp.processing &&
<Fragment>
<Form
onSubmit={this.handleFormSubmit}
initialValues={{
interface_name,
...values,
}}
interfaces={dhcp.interfaces}
processingConfig={dhcp.processingConfig}
processingInterfaces={dhcp.processingInterfaces}
enabled={enabled}
/>
<hr/>
<div className="card-actions mb-3">
{this.getToggleDhcpButton()}
<button
type="button"
className={statusButtonClass}
onClick={() =>
this.props.findActiveDhcp(interface_name)
}
disabled={
enabled
|| !interface_name
|| dhcp.processingConfig
}
>
<Trans>check_dhcp_servers</Trans>
</button>
</div>
{!enabled && dhcp.check &&
<Fragment>
{this.getStaticIpWarning(t, dhcp.check, interface_name)}
{this.getActiveDhcpMessage(t, dhcp.check)}
{this.getDhcpWarning(dhcp.check)}
</Fragment>
}
</Fragment>
}
</div>
</Card>
{!dhcp.processing && dhcp.config.enabled &&
<PageTitle title={t('dhcp_settings')} />
{(dhcp.processing || dhcp.processingInterfaces) && <Loading />}
{!dhcp.processing && !dhcp.processingInterfaces && (
<Fragment>
<Card title={ t('dhcp_leases') } bodyType="card-body box-body--settings">
<div className="row">
<div className="col">
<Leases leases={dhcp.leases} />
</div>
</div>
</Card>
<Card title={ t('dhcp_static_leases') } bodyType="card-body box-body--settings">
<div className="row">
<div className="col-12">
<StaticLeases
staticLeases={dhcp.staticLeases}
isModalOpen={dhcp.isModalOpen}
addStaticLease={this.props.addStaticLease}
removeStaticLease={this.props.removeStaticLease}
toggleLeaseModal={this.props.toggleLeaseModal}
processingAdding={dhcp.processingAdding}
processingDeleting={dhcp.processingDeleting}
<Card
title={t('dhcp_title')}
subtitle={t('dhcp_description')}
bodyType="card-body box-body--settings"
>
<div className="dhcp">
<Fragment>
<Form
onSubmit={this.handleFormSubmit}
initialValues={{
interface_name,
...values,
}}
interfaces={dhcp.interfaces}
processingConfig={dhcp.processingConfig}
processingInterfaces={dhcp.processingInterfaces}
enabled={enabled}
/>
</div>
<div className="col-12">
<button
type="button"
className="btn btn-success btn-standard mt-3"
onClick={() => this.props.toggleLeaseModal()}
>
<Trans>dhcp_add_static_lease</Trans>
</button>
</div>
<hr />
<div className="card-actions mb-3">
{this.getToggleDhcpButton()}
<button
type="button"
className={statusButtonClass}
onClick={() =>
this.props.findActiveDhcp(interface_name)
}
disabled={
enabled || !interface_name || dhcp.processingConfig
}
>
<Trans>check_dhcp_servers</Trans>
</button>
</div>
{!enabled && dhcp.check && (
<Fragment>
{this.getStaticIpWarning(t, dhcp.check, interface_name)}
{this.getActiveDhcpMessage(t, dhcp.check)}
{this.getDhcpWarning(dhcp.check)}
</Fragment>
)}
</Fragment>
</div>
</Card>
{dhcp.config.enabled && (
<Fragment>
<Card
title={t('dhcp_leases')}
bodyType="card-body box-body--settings"
>
<div className="row">
<div className="col">
<Leases leases={dhcp.leases} />
</div>
</div>
</Card>
<Card
title={t('dhcp_static_leases')}
bodyType="card-body box-body--settings"
>
<div className="row">
<div className="col-12">
<StaticLeases
staticLeases={dhcp.staticLeases}
isModalOpen={dhcp.isModalOpen}
addStaticLease={this.props.addStaticLease}
removeStaticLease={this.props.removeStaticLease}
toggleLeaseModal={this.props.toggleLeaseModal}
processingAdding={dhcp.processingAdding}
processingDeleting={dhcp.processingDeleting}
/>
</div>
<div className="col-12">
<button
type="button"
className="btn btn-success btn-standard mt-3"
onClick={() => this.props.toggleLeaseModal()}
>
<Trans>dhcp_add_static_lease</Trans>
</button>
</div>
</div>
</Card>
</Fragment>
)}
</Fragment>
}
)}
</Fragment>
);
}
@@ -256,10 +265,10 @@ Dhcp.propTypes = {
getDhcpStatus: PropTypes.func,
setDhcpConfig: PropTypes.func,
findActiveDhcp: PropTypes.func,
handleSubmit: PropTypes.func,
addStaticLease: PropTypes.func,
removeStaticLease: PropTypes.func,
toggleLeaseModal: PropTypes.func,
getDhcpInterfaces: PropTypes.func,
t: PropTypes.func,
};

View File

@@ -7,7 +7,12 @@ const Examples = props => (
<p>
<Trans
components={[
<a href="https://kb.adguard.com/general/dns-providers" target="_blank" rel="noopener noreferrer" key="0">
<a
href="https://kb.adguard.com/general/dns-providers"
target="_blank"
rel="noopener noreferrer"
key="0"
>
DNS providers
</a>,
]}
@@ -18,14 +23,19 @@ const Examples = props => (
<Trans>examples_title</Trans>:
<ol className="leading-loose">
<li>
<code>1.1.1.1</code> - { props.t('example_upstream_regular') }
<code>1.1.1.1</code> - {props.t('example_upstream_regular')}
</li>
<li>
<code>tls://1dot1dot1dot1.cloudflare-dns.com</code> &nbsp;
<span>
<Trans
components={[
<a href="https://en.wikipedia.org/wiki/DNS_over_TLS" target="_blank" rel="noopener noreferrer" key="0">
<a
href="https://en.wikipedia.org/wiki/DNS_over_TLS"
target="_blank"
rel="noopener noreferrer"
key="0"
>
DNS-over-TLS
</a>,
]}
@@ -39,7 +49,12 @@ const Examples = props => (
<span>
<Trans
components={[
<a href="https://en.wikipedia.org/wiki/DNS_over_HTTPS" target="_blank" rel="noopener noreferrer" key="0">
<a
href="https://en.wikipedia.org/wiki/DNS_over_HTTPS"
target="_blank"
rel="noopener noreferrer"
key="0"
>
DNS-over-HTTPS
</a>,
]}
@@ -56,13 +71,28 @@ const Examples = props => (
<span>
<Trans
components={[
<a href="https://dnscrypt.info/stamps/" target="_blank" rel="noopener noreferrer" key="0">
<a
href="https://dnscrypt.info/stamps/"
target="_blank"
rel="noopener noreferrer"
key="0"
>
DNS Stamps
</a>,
<a href="https://dnscrypt.info/" target="_blank" rel="noopener noreferrer" key="1">
<a
href="https://dnscrypt.info/"
target="_blank"
rel="noopener noreferrer"
key="1"
>
DNSCrypt
</a>,
<a href="https://en.wikipedia.org/wiki/DNS_over_HTTPS" target="_blank" rel="noopener noreferrer" key="2">
<a
href="https://en.wikipedia.org/wiki/DNS_over_HTTPS"
target="_blank"
rel="noopener noreferrer"
key="2"
>
DNS-over-HTTPS
</a>,
]}
@@ -76,7 +106,12 @@ const Examples = props => (
<span>
<Trans
components={[
<a href="https://github.com/AdguardTeam/AdGuardHome/wiki/Configuration#upstreams-for-domains" target="_blank" rel="noopener noreferrer" key="0">
<a
href="https://github.com/AdguardTeam/AdGuardHome/wiki/Configuration#upstreams-for-domains"
target="_blank"
rel="noopener noreferrer"
key="0"
>
Link
</a>,
]}

View File

@@ -6,7 +6,7 @@ import { Trans, withNamespaces } from 'react-i18next';
import flow from 'lodash/flow';
import classnames from 'classnames';
import { renderSelectField } from '../../../helpers/form';
import { renderSelectField } from '../../../../helpers/form';
import Examples from './Examples';
let Form = (props) => {
@@ -58,7 +58,7 @@ let Form = (props) => {
</div>
<div className="col-12">
<Examples />
<hr/>
<hr />
</div>
<div className="col-12">
<div className="form__group">
@@ -84,11 +84,13 @@ let Form = (props) => {
<button
type="button"
className={testButtonClass}
onClick={() => testUpstream({
upstream_dns: upstreamDns,
bootstrap_dns: bootstrapDns,
all_servers: allServers,
})}
onClick={() =>
testUpstream({
upstream_dns: upstreamDns,
bootstrap_dns: bootstrapDns,
all_servers: allServers,
})
}
disabled={!upstreamDns || processingTestUpstream}
>
<Trans>test_upstream_btn</Trans>
@@ -97,10 +99,7 @@ let Form = (props) => {
type="submit"
className="btn btn-success btn-standard"
disabled={
submitting
|| invalid
|| processingSetUpstream
|| processingTestUpstream
submitting || invalid || processingSetUpstream || processingTestUpstream
}
>
<Trans>apply_btn</Trans>
@@ -140,5 +139,7 @@ Form = connect((state) => {
export default flow([
withNamespaces(),
reduxForm({ form: 'upstreamForm' }),
reduxForm({
form: 'upstreamForm',
}),
])(Form);

View File

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { withNamespaces } from 'react-i18next';
import Form from './Form';
import Card from '../../ui/Card';
import Card from '../../../ui/Card';
class Upstream extends Component {
handleSubmit = (values) => {
@@ -12,7 +12,7 @@ class Upstream extends Component {
handleTest = (values) => {
this.props.testUpstream(values);
}
};
render() {
const {
@@ -26,8 +26,8 @@ class Upstream extends Component {
return (
<Card
title={ t('upstream_dns') }
subtitle={ t('upstream_dns_hint') }
title={t('upstream_dns')}
subtitle={t('upstream_dns_hint')}
bodyType="card-body box-body--settings"
>
<div className="row">

View File

@@ -0,0 +1,35 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { withNamespaces } from 'react-i18next';
import Upstream from './Upstream';
import PageTitle from '../../ui/PageTitle';
const Dns = (props) => {
const { dashboard, settings, t } = props;
return (
<Fragment>
<PageTitle title={t('dns_settings')} />
<Upstream
upstreamDns={dashboard.upstreamDns}
bootstrapDns={dashboard.bootstrapDns}
allServers={dashboard.allServers}
setUpstream={props.setUpstream}
testUpstream={props.testUpstream}
processingTestUpstream={settings.processingTestUpstream}
processingSetUpstream={settings.processingSetUpstream}
/>
</Fragment>
);
};
Dns.propTypes = {
dashboard: PropTypes.object.isRequired,
settings: PropTypes.object.isRequired,
setUpstream: PropTypes.func.isRequired,
testUpstream: PropTypes.func.isRequired,
t: PropTypes.func.isRequired,
};
export default withNamespaces()(Dns);

View File

@@ -66,14 +66,15 @@ let Form = (props) => {
setTlsConfig,
} = props;
const isSavingDisabled = invalid
|| submitting
|| processingConfig
|| processingValidate
|| (isEnabled && (!privateKey || !certificateChain))
|| (privateKey && !valid_key)
|| (certificateChain && !valid_cert)
|| (privateKey && certificateChain && !valid_pair);
const isSavingDisabled =
invalid ||
submitting ||
processingConfig ||
processingValidate ||
(isEnabled && (!privateKey || !certificateChain)) ||
(privateKey && !valid_key) ||
(certificateChain && !valid_cert) ||
(privateKey && certificateChain && !valid_pair);
return (
<form onSubmit={handleSubmit}>
@@ -91,7 +92,7 @@ let Form = (props) => {
<div className="form__desc">
<Trans>encryption_enable_desc</Trans>
</div>
<hr/>
<hr />
</div>
<div className="col-12">
<label className="form__label" htmlFor="server_name">
@@ -180,13 +181,20 @@ let Form = (props) => {
<div className="row">
<div className="col-12">
<div className="form__group form__group--settings">
<label className="form__label form__label--bold" htmlFor="certificate_chain">
<label
className="form__label form__label--bold"
htmlFor="certificate_chain"
>
<Trans>encryption_certificates</Trans>
</label>
<div className="form__desc form__desc--top">
<Trans
values={{ link: 'letsencrypt.org' }}
components={[<a href="https://letsencrypt.org/" key="0">link</a>]}
components={[
<a href="https://letsencrypt.org/" key="0">
link
</a>,
]}
>
encryption_certificates_desc
</Trans>
@@ -202,49 +210,52 @@ let Form = (props) => {
disabled={!isEnabled}
/>
<div className="form__status">
{certificateChain &&
{certificateChain && (
<Fragment>
<div className="form__label form__label--bold">
<Trans>encryption_status</Trans>:
</div>
<ul className="encryption__list">
<li className={valid_chain ? 'text-success' : 'text-danger'}>
{valid_chain ?
<li
className={valid_chain ? 'text-success' : 'text-danger'}
>
{valid_chain ? (
<Trans>encryption_chain_valid</Trans>
: <Trans>encryption_chain_invalid</Trans>
}
) : (
<Trans>encryption_chain_invalid</Trans>
)}
</li>
{valid_cert &&
{valid_cert && (
<Fragment>
{subject &&
{subject && (
<li>
<Trans>encryption_subject</Trans>:&nbsp;
{subject}
</li>
}
{issuer &&
)}
{issuer && (
<li>
<Trans>encryption_issuer</Trans>:&nbsp;
{issuer}
</li>
}
{not_after && not_after !== EMPTY_DATE &&
)}
{not_after && not_after !== EMPTY_DATE && (
<li>
<Trans>encryption_expire</Trans>:&nbsp;
{format(not_after, 'YYYY-MM-DD HH:mm:ss')}
</li>
}
{dns_names &&
)}
{dns_names && (
<li>
<Trans>encryption_hostnames</Trans>:&nbsp;
{dns_names}
</li>
}
)}
</Fragment>
}
)}
</ul>
</Fragment>
}
)}
</div>
</div>
</div>
@@ -266,35 +277,34 @@ let Form = (props) => {
disabled={!isEnabled}
/>
<div className="form__status">
{privateKey &&
{privateKey && (
<Fragment>
<div className="form__label form__label--bold">
<Trans>encryption_status</Trans>:
</div>
<ul className="encryption__list">
<li className={valid_key ? 'text-success' : 'text-danger'}>
{valid_key ?
{valid_key ? (
<Trans values={{ type: key_type }}>
encryption_key_valid
</Trans>
: <Trans values={{ type: key_type }}>
) : (
<Trans values={{ type: key_type }}>
encryption_key_invalid
</Trans>
}
)}
</li>
</ul>
</Fragment>
}
)}
</div>
</div>
</div>
{warning_validation &&
{warning_validation && (
<div className="col-12">
<p className="text-danger">
{warning_validation}
</p>
<p className="text-danger">{warning_validation}</p>
</div>
}
)}
</div>
<div className="btn-list mt-2">

View File

@@ -6,11 +6,17 @@ import debounce from 'lodash/debounce';
import { DEBOUNCE_TIMEOUT } from '../../../helpers/constants';
import Form from './Form';
import Card from '../../ui/Card';
import PageTitle from '../../ui/PageTitle';
import Loading from '../../ui/Loading';
class Encryption extends Component {
componentDidMount() {
if (this.props.encryption.enabled) {
this.props.validateTlsConfig(this.props.encryption);
const { getTlsStatus, validateTlsConfig, encryption } = this.props;
getTlsStatus();
if (encryption.enabled) {
validateTlsConfig(encryption);
}
}
@@ -36,7 +42,9 @@ class Encryption extends Component {
return (
<div className="encryption">
{encryption &&
<PageTitle title={t('encryption_settings')} />
{encryption.processing && <Loading />}
{!encryption.processing && (
<Card
title={t('encryption_title')}
subtitle={t('encryption_desc')}
@@ -58,7 +66,7 @@ class Encryption extends Component {
{...this.props.encryption}
/>
</Card>
}
)}
</div>
);
}

View File

@@ -2,12 +2,6 @@ import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import { withNamespaces, Trans } from 'react-i18next';
import Upstream from './Upstream';
import Dhcp from './Dhcp';
import Encryption from './Encryption';
import Clients from './Clients';
import AutoClients from './Clients/AutoClients';
import Access from './Access';
import Checkbox from '../ui/Checkbox';
import Loading from '../ui/Loading';
import PageTitle from '../ui/PageTitle';
@@ -41,10 +35,6 @@ class Settings extends Component {
componentDidMount() {
this.props.initSettings(this.settings);
this.props.getDhcpStatus();
this.props.getDhcpInterfaces();
this.props.getTlsStatus();
this.props.getAccessList();
}
renderSettings = (settings) => {
@@ -69,72 +59,20 @@ class Settings extends Component {
};
render() {
const {
settings, dashboard, clients, access, t,
} = this.props;
const { settings, t } = this.props;
return (
<Fragment>
<PageTitle title={t('settings')} />
<PageTitle title={t('general_settings')} />
{settings.processing && <Loading />}
{!settings.processing && (
<div className="content">
<div className="row">
<div className="col-md-12">
<Card
title={t('general_settings')}
bodyType="card-body box-body--settings"
>
<Card bodyType="card-body box-body--settings">
<div className="form">
{this.renderSettings(settings.settingsList)}
</div>
</Card>
<Upstream
upstreamDns={dashboard.upstreamDns}
bootstrapDns={dashboard.bootstrapDns}
allServers={dashboard.allServers}
setUpstream={this.props.setUpstream}
testUpstream={this.props.testUpstream}
processingTestUpstream={settings.processingTestUpstream}
processingSetUpstream={settings.processingSetUpstream}
/>
{!dashboard.processingTopStats && !dashboard.processingClients && (
<Fragment>
<Clients
clients={dashboard.clients}
topStats={dashboard.topStats}
isModalOpen={clients.isModalOpen}
modalClientName={clients.modalClientName}
modalType={clients.modalType}
addClient={this.props.addClient}
updateClient={this.props.updateClient}
deleteClient={this.props.deleteClient}
toggleClientModal={this.props.toggleClientModal}
processingAdding={clients.processingAdding}
processingDeleting={clients.processingDeleting}
processingUpdating={clients.processingUpdating}
/>
<AutoClients
autoClients={dashboard.autoClients}
topStats={dashboard.topStats}
/>
</Fragment>
)}
<Access access={access} setAccessList={this.props.setAccessList} />
<Encryption
encryption={this.props.encryption}
setTlsConfig={this.props.setTlsConfig}
validateTlsConfig={this.props.validateTlsConfig}
/>
<Dhcp
dhcp={this.props.dhcp}
toggleDhcp={this.props.toggleDhcp}
getDhcpStatus={this.props.getDhcpStatus}
findActiveDhcp={this.props.findActiveDhcp}
setDhcpConfig={this.props.setDhcpConfig}
addStaticLease={this.props.addStaticLease}
removeStaticLease={this.props.removeStaticLease}
toggleLeaseModal={this.props.toggleLeaseModal}
/>
</div>
</div>
</div>
@@ -149,8 +87,6 @@ Settings.propTypes = {
settings: PropTypes.object,
settingsList: PropTypes.object,
toggleSetting: PropTypes.func,
handleUpstreamChange: PropTypes.func,
setUpstream: PropTypes.func,
t: PropTypes.func,
};