+ client: handle DNS rewrites

This commit is contained in:
Ildar Kamalov
2019-07-22 15:32:12 +03:00
parent 70b8cf6ec8
commit e95aae5744
20 changed files with 631 additions and 70 deletions

View File

@@ -0,0 +1,89 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Field, reduxForm } from 'redux-form';
import { Trans, withNamespaces } from 'react-i18next';
import flow from 'lodash/flow';
import { renderField, required, domain, answer } from '../../../../helpers/form';
const Form = (props) => {
const {
t,
handleSubmit,
reset,
pristine,
submitting,
toggleRewritesModal,
processingAdd,
} = props;
return (
<form onSubmit={handleSubmit}>
<div className="modal-body">
<div className="form__group">
<Field
id="domain"
name="domain"
component={renderField}
type="text"
className="form-control"
placeholder={t('form_domain')}
validate={[required, domain]}
/>
</div>
<div className="form__group">
<Field
id="answer"
name="answer"
component={renderField}
type="text"
className="form-control"
placeholder={t('form_answer')}
validate={[required, answer]}
/>
</div>
</div>
<div className="modal-footer">
<div className="btn-list">
<button
type="button"
className="btn btn-secondary btn-standard"
disabled={submitting || processingAdd}
onClick={() => {
reset();
toggleRewritesModal();
}}
>
<Trans>cancel_btn</Trans>
</button>
<button
type="submit"
className="btn btn-success btn-standard"
disabled={submitting || pristine || processingAdd}
>
<Trans>save_btn</Trans>
</button>
</div>
</div>
</form>
);
};
Form.propTypes = {
pristine: PropTypes.bool.isRequired,
handleSubmit: PropTypes.func.isRequired,
reset: PropTypes.func.isRequired,
toggleRewritesModal: PropTypes.func.isRequired,
submitting: PropTypes.bool.isRequired,
processingAdd: PropTypes.bool.isRequired,
t: PropTypes.func.isRequired,
};
export default flow([
withNamespaces(),
reduxForm({
form: 'rewritesForm',
enableReinitialize: true,
}),
])(Form);

View File

@@ -0,0 +1,52 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Trans, withNamespaces } from 'react-i18next';
import ReactModal from 'react-modal';
import Form from './Form';
const Modal = (props) => {
const {
isModalOpen,
handleSubmit,
toggleRewritesModal,
processingAdd,
processingDelete,
} = props;
return (
<ReactModal
className="Modal__Bootstrap modal-dialog modal-dialog-centered"
closeTimeoutMS={0}
isOpen={isModalOpen}
onRequestClose={() => toggleRewritesModal()}
>
<div className="modal-content">
<div className="modal-header">
<h4 className="modal-title">
<Trans>Add DNS rewrite</Trans>
</h4>
<button type="button" className="close" onClick={() => toggleRewritesModal()}>
<span className="sr-only">Close</span>
</button>
</div>
<Form
onSubmit={handleSubmit}
toggleRewritesModal={toggleRewritesModal}
processingAdd={processingAdd}
processingDelete={processingDelete}
/>
</div>
</ReactModal>
);
};
Modal.propTypes = {
isModalOpen: PropTypes.bool.isRequired,
handleSubmit: PropTypes.func.isRequired,
toggleRewritesModal: PropTypes.func.isRequired,
processingAdd: PropTypes.bool.isRequired,
processingDelete: PropTypes.bool.isRequired,
};
export default withNamespaces()(Modal);

View File

@@ -0,0 +1,87 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import ReactTable from 'react-table';
import { withNamespaces } from 'react-i18next';
class Table extends Component {
cellWrap = ({ value }) => (
<div className="logs__row logs__row--overflow">
<span className="logs__text" title={value}>
{value}
</span>
</div>
);
columns = [
{
Header: 'Domain',
accessor: 'domain',
Cell: this.cellWrap,
},
{
Header: 'Answer',
accessor: 'answer',
Cell: this.cellWrap,
},
{
Header: this.props.t('actions_table_header'),
accessor: 'actions',
maxWidth: 100,
Cell: value => (
<div className="logs__row logs__row--center">
<button
type="button"
className="btn btn-icon btn-outline-secondary btn-sm"
onClick={() =>
this.props.handleDelete({
answer: value.row.answer,
domain: value.row.domain,
})
}
title={this.props.t('delete_table_action')}
>
<svg className="icons">
<use xlinkHref="#delete" />
</svg>
</button>
</div>
),
},
];
render() {
const {
t, list, processing, processingAdd, processingDelete,
} = this.props;
return (
<ReactTable
data={list || []}
columns={this.columns}
loading={processing || processingAdd || processingDelete}
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('rewrite_not_found')}
/>
);
}
}
Table.propTypes = {
t: PropTypes.func.isRequired,
list: PropTypes.array.isRequired,
processing: PropTypes.bool.isRequired,
processingAdd: PropTypes.bool.isRequired,
processingDelete: PropTypes.bool.isRequired,
handleDelete: PropTypes.func.isRequired,
};
export default withNamespaces()(Table);

View File

@@ -0,0 +1,83 @@
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import { Trans, withNamespaces } from 'react-i18next';
import Table from './Table';
import Modal from './Modal';
import Card from '../../../ui/Card';
class Rewrites extends Component {
handleSubmit = (values) => {
this.props.addRewrite(values);
};
handleDelete = (values) => {
// eslint-disable-next-line no-alert
if (window.confirm(this.props.t('rewrite_confirm_delete', { key: values.domain }))) {
this.props.deleteRewrite(values);
}
};
render() {
const {
t,
rewrites,
toggleRewritesModal,
} = this.props;
const {
list,
isModalOpen,
processing,
processingAdd,
processingDelete,
} = rewrites;
return (
<Card
id="rewrites"
title={t('dns_rewrites')}
subtitle={t('rewrite_desc')}
bodyType="card-body box-body--settings"
>
<Fragment>
<Table
list={list}
processing={processing}
processingAdd={processingAdd}
processingDelete={processingDelete}
handleDelete={this.handleDelete}
/>
<button
type="button"
className="btn btn-success btn-standard mt-3"
onClick={() => toggleRewritesModal()}
disabled={processingAdd}
>
<Trans>rewrite_add</Trans>
</button>
<Modal
isModalOpen={isModalOpen}
toggleRewritesModal={toggleRewritesModal}
handleSubmit={this.handleSubmit}
processingAdd={processingAdd}
processingDelete={processingDelete}
/>
</Fragment>
</Card>
);
}
}
Rewrites.propTypes = {
t: PropTypes.func.isRequired,
getRewritesList: PropTypes.func.isRequired,
toggleRewritesModal: PropTypes.func.isRequired,
addRewrite: PropTypes.func.isRequired,
deleteRewrite: PropTypes.func.isRequired,
rewrites: PropTypes.object.isRequired,
};
export default withNamespaces()(Rewrites);

View File

@@ -4,12 +4,14 @@ import { withNamespaces } from 'react-i18next';
import Upstream from './Upstream';
import Access from './Access';
import Rewrites from './Rewrites';
import PageTitle from '../../ui/PageTitle';
import Loading from '../../ui/Loading';
class Dns extends Component {
componentDidMount() {
this.props.getAccessList();
this.props.getRewritesList();
}
render() {
@@ -18,9 +20,14 @@ class Dns extends Component {
dashboard,
settings,
access,
rewrites,
setAccessList,
testUpstream,
setUpstream,
getRewritesList,
addRewrite,
deleteRewrite,
toggleRewritesModal,
} = this.props;
return (
@@ -39,6 +46,13 @@ class Dns extends Component {
testUpstream={testUpstream}
/>
<Access access={access} setAccessList={setAccessList} />
<Rewrites
rewrites={rewrites}
getRewritesList={getRewritesList}
addRewrite={addRewrite}
deleteRewrite={deleteRewrite}
toggleRewritesModal={toggleRewritesModal}
/>
</Fragment>
)}
</Fragment>
@@ -54,6 +68,11 @@ Dns.propTypes = {
getAccessList: PropTypes.func.isRequired,
setAccessList: PropTypes.func.isRequired,
access: PropTypes.object.isRequired,
rewrites: PropTypes.object.isRequired,
getRewritesList: PropTypes.func.isRequired,
addRewrite: PropTypes.func.isRequired,
deleteRewrite: PropTypes.func.isRequired,
toggleRewritesModal: PropTypes.func.isRequired,
t: PropTypes.func.isRequired,
};