Added initial layout for DHCP server config
This commit is contained in:
parent
390883126c
commit
dd21f497e3
|
@ -4126,6 +4126,11 @@
|
||||||
"next-tick": "1"
|
"next-tick": "1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"es6-error": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg=="
|
||||||
|
},
|
||||||
"es6-iterator": {
|
"es6-iterator": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
|
||||||
|
@ -6588,7 +6593,7 @@
|
||||||
},
|
},
|
||||||
"html-webpack-plugin": {
|
"html-webpack-plugin": {
|
||||||
"version": "3.2.0",
|
"version": "3.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz",
|
"resolved": "http://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz",
|
||||||
"integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=",
|
"integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@ -6638,7 +6643,7 @@
|
||||||
},
|
},
|
||||||
"readable-stream": {
|
"readable-stream": {
|
||||||
"version": "1.0.34",
|
"version": "1.0.34",
|
||||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
|
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
|
||||||
"integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
|
"integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@ -7387,8 +7392,7 @@
|
||||||
"is-promise": {
|
"is-promise": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
|
||||||
"integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
|
"integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"is-regex": {
|
"is-regex": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
|
@ -13202,6 +13206,21 @@
|
||||||
"reduce-reducers": "^0.1.0"
|
"reduce-reducers": "^0.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"redux-form": {
|
||||||
|
"version": "7.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/redux-form/-/redux-form-7.4.2.tgz",
|
||||||
|
"integrity": "sha512-QxC36s4Lelx5Cr8dbpxqvl23dwYOydeAX8c6YPmgkz/Dhj053C16S2qoyZN6LO6HJ2oUF00rKsAyE94GwOUhFA==",
|
||||||
|
"requires": {
|
||||||
|
"es6-error": "^4.1.1",
|
||||||
|
"hoist-non-react-statics": "^2.5.4",
|
||||||
|
"invariant": "^2.2.4",
|
||||||
|
"is-promise": "^2.1.0",
|
||||||
|
"lodash": "^4.17.10",
|
||||||
|
"lodash-es": "^4.17.10",
|
||||||
|
"prop-types": "^15.6.1",
|
||||||
|
"react-lifecycles-compat": "^3.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"redux-thunk": {
|
"redux-thunk": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz",
|
||||||
|
@ -15003,7 +15022,7 @@
|
||||||
},
|
},
|
||||||
"through": {
|
"through": {
|
||||||
"version": "2.3.8",
|
"version": "2.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
"resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||||
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
|
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
"react-transition-group": "^2.4.0",
|
"react-transition-group": "^2.4.0",
|
||||||
"redux": "^4.0.0",
|
"redux": "^4.0.0",
|
||||||
"redux-actions": "^2.4.0",
|
"redux-actions": "^2.4.0",
|
||||||
|
"redux-form": "^7.4.2",
|
||||||
"redux-thunk": "^2.3.0",
|
"redux-thunk": "^2.3.0",
|
||||||
"svg-url-loader": "^2.3.2",
|
"svg-url-loader": "^2.3.2",
|
||||||
"whatwg-fetch": "2.0.3"
|
"whatwg-fetch": "2.0.3"
|
||||||
|
|
|
@ -127,5 +127,16 @@
|
||||||
"category_label": "Category",
|
"category_label": "Category",
|
||||||
"rule_label": "Rule",
|
"rule_label": "Rule",
|
||||||
"filter_label": "Filter",
|
"filter_label": "Filter",
|
||||||
"unknown_filter": "Unknown filter {{filterId}}"
|
"unknown_filter": "Unknown filter {{filterId}}",
|
||||||
|
"refresh_status": "Refresh status",
|
||||||
|
"save_config": "Save config",
|
||||||
|
"enabled_dhcp": "DHCP server enabled",
|
||||||
|
"disabled_dhcp": "DHCP server disabled",
|
||||||
|
"dhcp_title": "DHCP server",
|
||||||
|
"dhcp_description": "If your router does not provide DHCP settings, you can use AdGuard's own built-in DHCP server.",
|
||||||
|
"dhcp_enable": "Enable DHCP server",
|
||||||
|
"dhcp_disable": "Disable DHCP server",
|
||||||
|
"dhcp_not_found": "No active DHCP servers found on the network. It is safe to enable the built-in DHCP server.",
|
||||||
|
"dhcp_leases": "DHCP leases",
|
||||||
|
"dhcp_leases_not_found": "No DHCP leases found"
|
||||||
}
|
}
|
|
@ -522,3 +522,73 @@ export const getLanguage = () => async (dispatch) => {
|
||||||
dispatch(getLanguageFailure());
|
dispatch(getLanguageFailure());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getDhcpStatusRequest = createAction('GET_DHCP_STATUS_REQUEST');
|
||||||
|
export const getDhcpStatusSuccess = createAction('GET_DHCP_STATUS_SUCCESS');
|
||||||
|
export const getDhcpStatusFailure = createAction('GET_DHCP_STATUS_FAILURE');
|
||||||
|
|
||||||
|
export const getDhcpStatus = () => async (dispatch) => {
|
||||||
|
dispatch(getDhcpStatusRequest());
|
||||||
|
try {
|
||||||
|
const status = await apiClient.getDhcpStatus();
|
||||||
|
dispatch(getDhcpStatusSuccess(status));
|
||||||
|
} catch (error) {
|
||||||
|
dispatch(addErrorToast({ error }));
|
||||||
|
dispatch(getDhcpStatusFailure());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setDhcpConfigRequest = createAction('SET_DHCP_CONFIG_REQUEST');
|
||||||
|
export const setDhcpConfigSuccess = createAction('SET_DHCP_CONFIG_SUCCESS');
|
||||||
|
export const setDhcpConfigFailure = createAction('SET_DHCP_CONFIG_FAILURE');
|
||||||
|
|
||||||
|
export const setDhcpConfig = config => async (dispatch) => {
|
||||||
|
dispatch(setDhcpConfigRequest());
|
||||||
|
try {
|
||||||
|
await apiClient.setDhcpConfig(config);
|
||||||
|
dispatch(setDhcpConfigSuccess());
|
||||||
|
} catch (error) {
|
||||||
|
dispatch(addErrorToast({ error }));
|
||||||
|
dispatch(setDhcpConfigFailure());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const findActiveDhcpRequest = createAction('FIND_ACTIVE_DHCP_REQUEST');
|
||||||
|
export const findActiveDhcpSuccess = createAction('FIND_ACTIVE_DHCP_SUCCESS');
|
||||||
|
export const findActiveDhcpFailure = createAction('FIND_ACTIVE_DHCP_FAILURE');
|
||||||
|
|
||||||
|
export const findActiveDhcp = () => async (dispatch) => {
|
||||||
|
dispatch(findActiveDhcpRequest());
|
||||||
|
try {
|
||||||
|
const result = await apiClient.findActiveDhcp();
|
||||||
|
dispatch(findActiveDhcpSuccess(result));
|
||||||
|
} catch (error) {
|
||||||
|
dispatch(addErrorToast({ error }));
|
||||||
|
dispatch(findActiveDhcpFailure());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const toggleDhcpRequest = createAction('TOGGLE_DHCP_REQUEST');
|
||||||
|
export const toggleDhcpFailure = createAction('TOGGLE_DHCP_FAILURE');
|
||||||
|
export const toggleDhcpSuccess = createAction('TOGGLE_DHCP_SUCCESS');
|
||||||
|
|
||||||
|
export const toggleDhcp = status => async (dispatch) => {
|
||||||
|
dispatch(toggleDhcpRequest());
|
||||||
|
let successMessage = '';
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (status) {
|
||||||
|
successMessage = 'disabled_dhcp';
|
||||||
|
await apiClient.disableGlobalProtection();
|
||||||
|
} else {
|
||||||
|
successMessage = 'enabled_dhcp';
|
||||||
|
await apiClient.enableGlobalProtection();
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(addSuccessToast(successMessage));
|
||||||
|
dispatch(toggleDhcpSuccess());
|
||||||
|
} catch (error) {
|
||||||
|
dispatch(addErrorToast({ error }));
|
||||||
|
dispatch(toggleDhcpFailure());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -302,4 +302,71 @@ export default class Api {
|
||||||
};
|
};
|
||||||
return this.makeRequest(path, method, parameters);
|
return this.makeRequest(path, method, parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DHCP
|
||||||
|
DHCP_STATUS = { path: 'dhcp/status', method: 'GET' };
|
||||||
|
DHCP_SET_CONFIG = { path: 'dhcp/set_config', method: 'POST' };
|
||||||
|
DHCP_FIND_ACTIVE = { path: 'dhcp/find_active_dhcp', method: 'GET' };
|
||||||
|
|
||||||
|
getDhcpStatus() {
|
||||||
|
// const { path, method } = this.DHCP_STATUS;
|
||||||
|
// return this.makeRequest(path, method);
|
||||||
|
|
||||||
|
const example = {
|
||||||
|
config: {
|
||||||
|
enabled: false,
|
||||||
|
gateway_ip: '192.168.1.1',
|
||||||
|
subnet_mask: '255.255.255.0',
|
||||||
|
range_start: '192.168.1.2',
|
||||||
|
range_end: '192.168.10.50',
|
||||||
|
lease_duration: '43200',
|
||||||
|
},
|
||||||
|
leases: [
|
||||||
|
{
|
||||||
|
mac: '001109b3b3b8',
|
||||||
|
ip: '192.168.1.22',
|
||||||
|
hostname: 'dell',
|
||||||
|
expires: '2017-07-21T17:32:28Z',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mac: '001109b3b3b9',
|
||||||
|
ip: '192.168.1.23',
|
||||||
|
hostname: 'dell',
|
||||||
|
expires: '2017-07-21T17:32:28Z',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve(example);
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setDhcpConfig(config) {
|
||||||
|
// const { path, method } = this.DHCP_SET_CONFIG;
|
||||||
|
// const parameters = config;
|
||||||
|
// return this.makeRequest(path, method, parameters);
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve(window.alert(`Set config:\n\n${JSON.stringify(config, null, 2)}`));
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
findActiveDhcp() {
|
||||||
|
// const { path, method } = this.DHCP_FIND_ACTIVE;
|
||||||
|
// return this.makeRequest(path, method);
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve({
|
||||||
|
gateway_ip: '127.0.0.1',
|
||||||
|
found: true,
|
||||||
|
});
|
||||||
|
}, 10000);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
import React, { Fragment } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Field, reduxForm } from 'redux-form';
|
||||||
|
import { R_IPV4 } from '../../../helpers/constants';
|
||||||
|
|
||||||
|
const required = (value) => {
|
||||||
|
if (value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return 'Required field';
|
||||||
|
};
|
||||||
|
|
||||||
|
const ipv4 = (value) => {
|
||||||
|
if (value && !new RegExp(R_IPV4).test(value)) {
|
||||||
|
return 'Invalid IPv4 format';
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderField = ({
|
||||||
|
input, className, placeholder, type, disabled, meta: { touched, error },
|
||||||
|
}) => (
|
||||||
|
<Fragment>
|
||||||
|
<input
|
||||||
|
{...input}
|
||||||
|
placeholder={placeholder}
|
||||||
|
type={type}
|
||||||
|
className={className}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
{!disabled && touched && (error && <span className="form__message form__message--error">{error}</span>)}
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
|
||||||
|
const Form = (props) => {
|
||||||
|
const {
|
||||||
|
handleSubmit, pristine, submitting, enabled,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-lg-6">
|
||||||
|
<div className="form__group form__group--dhcp">
|
||||||
|
<label>Gateway IP</label>
|
||||||
|
<Field
|
||||||
|
name="gateway_ip"
|
||||||
|
component={renderField}
|
||||||
|
type="text"
|
||||||
|
className="form-control"
|
||||||
|
placeholder="Gateway IP"
|
||||||
|
validate={[ipv4, required]}
|
||||||
|
disabled={!enabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="form__group form__group--dhcp">
|
||||||
|
<label>Subnet mask</label>
|
||||||
|
<Field
|
||||||
|
name="subnet_mask"
|
||||||
|
component={renderField}
|
||||||
|
type="text"
|
||||||
|
className="form-control"
|
||||||
|
placeholder="Subnet mask"
|
||||||
|
validate={[ipv4, required]}
|
||||||
|
disabled={!enabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-lg-6">
|
||||||
|
<div className="form__group form__group--dhcp">
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-12">
|
||||||
|
<label>Range of IP addresses</label>
|
||||||
|
</div>
|
||||||
|
<div className="col">
|
||||||
|
<Field
|
||||||
|
name="range_start"
|
||||||
|
component={renderField}
|
||||||
|
type="text"
|
||||||
|
className="form-control"
|
||||||
|
placeholder="Range start"
|
||||||
|
validate={[ipv4, required]}
|
||||||
|
disabled={!enabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="col">
|
||||||
|
<Field
|
||||||
|
name="range_end"
|
||||||
|
component={renderField}
|
||||||
|
type="text"
|
||||||
|
className="form-control"
|
||||||
|
placeholder="Range end"
|
||||||
|
validate={[ipv4, required]}
|
||||||
|
disabled={!enabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="form__group form__group--dhcp">
|
||||||
|
<label>DHCP lease time (in seconds)</label>
|
||||||
|
<Field
|
||||||
|
name="lease_duration"
|
||||||
|
component={renderField}
|
||||||
|
type="number"
|
||||||
|
className="form-control"
|
||||||
|
placeholder="Lease duration"
|
||||||
|
validate={[required]}
|
||||||
|
disabled={!enabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="btn btn-success btn-standart"
|
||||||
|
disabled={pristine || submitting || !enabled}
|
||||||
|
>
|
||||||
|
Save config
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Form.propTypes = {
|
||||||
|
handleSubmit: PropTypes.func,
|
||||||
|
pristine: PropTypes.bool,
|
||||||
|
submitting: PropTypes.bool,
|
||||||
|
enabled: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default reduxForm({
|
||||||
|
form: 'dhcpForm',
|
||||||
|
})(Form);
|
|
@ -0,0 +1,36 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import ReactTable from 'react-table';
|
||||||
|
import { withNamespaces } from 'react-i18next';
|
||||||
|
|
||||||
|
const columns = [{
|
||||||
|
Header: 'MAC',
|
||||||
|
accessor: 'mac',
|
||||||
|
}, {
|
||||||
|
Header: 'IP',
|
||||||
|
accessor: 'ip',
|
||||||
|
}, {
|
||||||
|
Header: 'Hostname',
|
||||||
|
accessor: 'hostname',
|
||||||
|
}, {
|
||||||
|
Header: 'Expires',
|
||||||
|
accessor: 'expires',
|
||||||
|
}];
|
||||||
|
|
||||||
|
const Leases = props => (
|
||||||
|
<ReactTable
|
||||||
|
data={props.leases}
|
||||||
|
columns={columns}
|
||||||
|
showPagination={false}
|
||||||
|
noDataText={ props.t('dhcp_leases_not_found') }
|
||||||
|
minRows={6}
|
||||||
|
className="-striped -highlight card-table-overflow"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
Leases.propTypes = {
|
||||||
|
leases: PropTypes.array,
|
||||||
|
t: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withNamespaces()(Leases);
|
|
@ -0,0 +1,93 @@
|
||||||
|
import React, { Component, Fragment } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
import { Trans, withNamespaces } from 'react-i18next';
|
||||||
|
|
||||||
|
import Form from './Form';
|
||||||
|
import Leases from './Leases';
|
||||||
|
import Card from '../../ui/Card';
|
||||||
|
|
||||||
|
class Dhcp extends Component {
|
||||||
|
handleFormSubmit = (values) => {
|
||||||
|
this.props.setDhcpConfig(values);
|
||||||
|
};
|
||||||
|
|
||||||
|
handleRefresh = () => {
|
||||||
|
this.props.findActiveDhcp();
|
||||||
|
}
|
||||||
|
|
||||||
|
getToggleDhcpButton = () => {
|
||||||
|
const { enabled } = this.props.dhcp.config;
|
||||||
|
const buttonText = enabled ? 'dhcp_disable' : 'dhcp_enable';
|
||||||
|
const buttonClass = enabled ? 'btn-gray' : 'btn-success';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button type="button" className={`btn btn-standart mr-2 ${buttonClass}`} onClick={() => this.props.toggleDhcp(enabled)}>
|
||||||
|
<Trans>{buttonText}</Trans>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { t, dhcp } = this.props;
|
||||||
|
const statusButtonClass = classnames({
|
||||||
|
'btn btn-primary btn-standart': true,
|
||||||
|
'btn btn-primary btn-standart btn-loading': dhcp.processingStatus,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
{!dhcp.processing &&
|
||||||
|
<Card title={ t('dhcp_title') } subtitle={ t('dhcp_description') } bodyType="card-body box-body--settings">
|
||||||
|
<div className="row">
|
||||||
|
<div className="col">
|
||||||
|
<div className="card-actions mt-0 mb-3">
|
||||||
|
{this.getToggleDhcpButton()}
|
||||||
|
<button
|
||||||
|
className={statusButtonClass}
|
||||||
|
type="button"
|
||||||
|
onClick={this.handleRefresh}
|
||||||
|
>
|
||||||
|
<Trans>refresh_status</Trans>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{dhcp.active && !dhcp.active.found &&
|
||||||
|
<div className="text-secondary">
|
||||||
|
<Trans>dhcp_not_found</Trans>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<hr/>
|
||||||
|
<Form
|
||||||
|
onSubmit={this.handleFormSubmit}
|
||||||
|
initialValues={dhcp.config}
|
||||||
|
enabled={dhcp.config.enabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
}
|
||||||
|
{!dhcp.processing && dhcp.config.enabled &&
|
||||||
|
<Card title={ t('dhcp_leases') } bodyType="card-body box-body--settings">
|
||||||
|
<div className="row">
|
||||||
|
<div className="col">
|
||||||
|
<Leases leases={dhcp.leases} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
}
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Dhcp.propTypes = {
|
||||||
|
dhcp: PropTypes.object,
|
||||||
|
toggleDhcp: PropTypes.func,
|
||||||
|
getDhcpStatus: PropTypes.func,
|
||||||
|
setDhcpConfig: PropTypes.func,
|
||||||
|
findActiveDhcp: PropTypes.func,
|
||||||
|
handleSubmit: PropTypes.func,
|
||||||
|
t: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withNamespaces()(Dhcp);
|
|
@ -1,4 +1,5 @@
|
||||||
.form__group {
|
.form__group {
|
||||||
|
position: relative;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +7,10 @@
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form__group--dhcp:last-child {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
.btn-standart {
|
.btn-standart {
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
padding-right: 20px;
|
padding-right: 20px;
|
||||||
|
@ -18,3 +23,11 @@
|
||||||
.form-control--textarea-large {
|
.form-control--textarea-large {
|
||||||
min-height: 240px;
|
min-height: 240px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form__message {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form__message--error {
|
||||||
|
color: #cd201f;
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import React, { Component, Fragment } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { withNamespaces, Trans } from 'react-i18next';
|
import { withNamespaces, Trans } from 'react-i18next';
|
||||||
import Upstream from './Upstream';
|
import Upstream from './Upstream';
|
||||||
|
import Dhcp from './Dhcp';
|
||||||
import Checkbox from '../ui/Checkbox';
|
import Checkbox from '../ui/Checkbox';
|
||||||
import Loading from '../ui/Loading';
|
import Loading from '../ui/Loading';
|
||||||
import PageTitle from '../ui/PageTitle';
|
import PageTitle from '../ui/PageTitle';
|
||||||
|
@ -34,6 +35,7 @@ class Settings extends Component {
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.props.initSettings(this.settings);
|
this.props.initSettings(this.settings);
|
||||||
|
this.props.getDhcpStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUpstreamChange = (value) => {
|
handleUpstreamChange = (value) => {
|
||||||
|
@ -92,6 +94,13 @@ class Settings extends Component {
|
||||||
handleUpstreamSubmit={this.handleUpstreamSubmit}
|
handleUpstreamSubmit={this.handleUpstreamSubmit}
|
||||||
handleUpstreamTest={this.handleUpstreamTest}
|
handleUpstreamTest={this.handleUpstreamTest}
|
||||||
/>
|
/>
|
||||||
|
<Dhcp
|
||||||
|
dhcp={this.props.dhcp}
|
||||||
|
toggleDhcp={this.props.toggleDhcp}
|
||||||
|
getDhcpStatus={this.props.getDhcpStatus}
|
||||||
|
findActiveDhcp={this.props.findActiveDhcp}
|
||||||
|
setDhcpConfig={this.props.setDhcpConfig}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,10 +1,21 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { initSettings, toggleSetting, handleUpstreamChange, setUpstream, testUpstream, addErrorToast } from '../actions';
|
import {
|
||||||
|
initSettings,
|
||||||
|
toggleSetting,
|
||||||
|
handleUpstreamChange,
|
||||||
|
setUpstream,
|
||||||
|
testUpstream,
|
||||||
|
addErrorToast,
|
||||||
|
toggleDhcp,
|
||||||
|
getDhcpStatus,
|
||||||
|
setDhcpConfig,
|
||||||
|
findActiveDhcp,
|
||||||
|
} from '../actions';
|
||||||
import Settings from '../components/Settings';
|
import Settings from '../components/Settings';
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = (state) => {
|
||||||
const { settings, dashboard } = state;
|
const { settings, dashboard, dhcp } = state;
|
||||||
const props = { settings, dashboard };
|
const props = { settings, dashboard, dhcp };
|
||||||
return props;
|
return props;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -15,6 +26,10 @@ const mapDispatchToProps = {
|
||||||
setUpstream,
|
setUpstream,
|
||||||
testUpstream,
|
testUpstream,
|
||||||
addErrorToast,
|
addErrorToast,
|
||||||
|
toggleDhcp,
|
||||||
|
getDhcpStatus,
|
||||||
|
setDhcpConfig,
|
||||||
|
findActiveDhcp,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
export const R_URL_REQUIRES_PROTOCOL = /^https?:\/\/\w[\w_\-.]*\.[a-z]{2,8}[^\s]*$/;
|
export const R_URL_REQUIRES_PROTOCOL = /^https?:\/\/\w[\w_\-.]*\.[a-z]{2,8}[^\s]*$/;
|
||||||
|
export const R_IPV4 = /^(?:(?:^|\.)(?:2(?:5[0-5]|[0-4]\d)|1?\d?\d)){4}$/g;
|
||||||
|
|
||||||
export const STATS_NAMES = {
|
export const STATS_NAMES = {
|
||||||
avg_processing_time: 'average_processing_time',
|
avg_processing_time: 'average_processing_time',
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { combineReducers } from 'redux';
|
||||||
import { handleActions } from 'redux-actions';
|
import { handleActions } from 'redux-actions';
|
||||||
import { loadingBarReducer } from 'react-redux-loading-bar';
|
import { loadingBarReducer } from 'react-redux-loading-bar';
|
||||||
import nanoid from 'nanoid';
|
import nanoid from 'nanoid';
|
||||||
|
import { reducer as formReducer } from 'redux-form';
|
||||||
import versionCompare from '../helpers/versionCompare';
|
import versionCompare from '../helpers/versionCompare';
|
||||||
|
|
||||||
import * as actions from '../actions';
|
import * as actions from '../actions';
|
||||||
|
@ -35,6 +36,7 @@ const settings = handleActions({
|
||||||
processing: true,
|
processing: true,
|
||||||
processingTestUpstream: false,
|
processingTestUpstream: false,
|
||||||
processingSetUpstream: false,
|
processingSetUpstream: false,
|
||||||
|
processingDhcpStatus: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const dashboard = handleActions({
|
const dashboard = handleActions({
|
||||||
|
@ -258,11 +260,44 @@ const toasts = handleActions({
|
||||||
},
|
},
|
||||||
}, { notices: [] });
|
}, { notices: [] });
|
||||||
|
|
||||||
|
const dhcp = handleActions({
|
||||||
|
[actions.getDhcpStatusRequest]: state => ({ ...state, processing: true }),
|
||||||
|
[actions.getDhcpStatusFailure]: state => ({ ...state, processing: false }),
|
||||||
|
[actions.getDhcpStatusSuccess]: (state, { payload }) => {
|
||||||
|
const newState = {
|
||||||
|
...state,
|
||||||
|
...payload,
|
||||||
|
processing: false,
|
||||||
|
};
|
||||||
|
return newState;
|
||||||
|
},
|
||||||
|
|
||||||
|
[actions.findActiveDhcpRequest]: state => ({ ...state, processingStatus: true }),
|
||||||
|
[actions.findActiveDhcpFailure]: state => ({ ...state, processingStatus: false }),
|
||||||
|
[actions.findActiveDhcpSuccess]: (state, { payload }) => ({
|
||||||
|
...state,
|
||||||
|
active: payload,
|
||||||
|
processingStatus: false,
|
||||||
|
}),
|
||||||
|
|
||||||
|
[actions.toggleDhcpSuccess]: (state) => {
|
||||||
|
const { config } = state;
|
||||||
|
const newConfig = { ...config, enabled: !config.enabled };
|
||||||
|
const newState = { ...state, config: newConfig };
|
||||||
|
return newState;
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
processing: true,
|
||||||
|
processingStatus: false,
|
||||||
|
});
|
||||||
|
|
||||||
export default combineReducers({
|
export default combineReducers({
|
||||||
settings,
|
settings,
|
||||||
dashboard,
|
dashboard,
|
||||||
queryLogs,
|
queryLogs,
|
||||||
filtering,
|
filtering,
|
||||||
toasts,
|
toasts,
|
||||||
|
dhcp,
|
||||||
loadingBar: loadingBarReducer,
|
loadingBar: loadingBarReducer,
|
||||||
|
form: formReducer,
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue