diff --git a/src/pages/Organizations/components/OrganizationForm.jsx b/src/pages/Organizations/components/OrganizationForm.jsx
new file mode 100644
index 0000000000..5588f6c1cf
--- /dev/null
+++ b/src/pages/Organizations/components/OrganizationForm.jsx
@@ -0,0 +1,182 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { withRouter } from 'react-router-dom';
+import { Formik, Field } from 'formik';
+import { I18n } from '@lingui/react';
+import { t } from '@lingui/macro';
+import {
+ Form,
+ FormGroup,
+} from '@patternfly/react-core';
+
+import { ConfigContext } from '../../../context';
+import FormField from '../../../components/FormField';
+import FormActionGroup from '../../../components/FormActionGroup';
+import AnsibleSelect from '../../../components/AnsibleSelect';
+import InstanceGroupsLookup from './InstanceGroupsLookup';
+import { required } from '../../../util/validators';
+
+class OrganizationForm extends Component {
+ constructor (props) {
+ super(props);
+
+ this.getRelatedInstanceGroups = this.getRelatedInstanceGroups.bind(this);
+ this.handleInstanceGroupsChange = this.handleInstanceGroupsChange.bind(this);
+ this.handleSubmit = this.handleSubmit.bind(this);
+
+ this.state = {
+ instanceGroups: [],
+ formIsValid: true,
+ };
+ }
+
+ async componentDidMount () {
+ let instanceGroups = [];
+
+ if (!this.isEditingNewOrganization()) {
+ try {
+ instanceGroups = await this.getRelatedInstanceGroups();
+ } catch (err) {
+ this.setState({ error: err });
+ }
+ }
+
+ this.setState({
+ instanceGroups,
+ initialInstanceGroups: [...instanceGroups],
+ });
+ }
+
+ async getRelatedInstanceGroups () {
+ const {
+ api,
+ organization: { id }
+ } = this.props;
+ const { data } = await api.getOrganizationInstanceGroups(id);
+ return data.results;
+ }
+
+ isEditingNewOrganization () {
+ const { organization } = this.props;
+ return !organization.id;
+ }
+
+ handleInstanceGroupsChange (instanceGroups) {
+ this.setState({ instanceGroups });
+ }
+
+ async handleSubmit (values) {
+ const { handleSubmit } = this.props;
+ const { instanceGroups, initialInstanceGroups } = this.state;
+
+ const initialIds = initialInstanceGroups.map(ig => ig.id);
+ const updatedIds = instanceGroups.map(ig => ig.id);
+ const groupsToAssociate = [...updatedIds]
+ .filter(x => !initialIds.includes(x));
+ const groupsToDisassociate = [...initialIds]
+ .filter(x => !updatedIds.includes(x));
+
+ handleSubmit(values, groupsToAssociate, groupsToDisassociate);
+ }
+
+ render () {
+ const { api, organization, handleCancel } = this.props;
+ const { instanceGroups, formIsValid, error } = this.state;
+ const defaultVenv = '/venv/ansible/';
+
+ return (
+
+ {({ i18n }) => (
+ (
+
+ )}
+ />
+ )}
+
+ );
+ }
+}
+
+OrganizationForm.propTypes = {
+ api: PropTypes.shape().isRequired,
+ organization: PropTypes.shape(),
+ handleSubmit: PropTypes.func.isRequired,
+ handleCancel: PropTypes.func.isRequired,
+};
+
+OrganizationForm.defaultProps = {
+ organization: {
+ name: '',
+ description: '',
+ custom_virtualenv: '',
+ }
+};
+
+OrganizationForm.contextTypes = {
+ custom_virtualenvs: PropTypes.arrayOf(PropTypes.string)
+};
+
+export default withRouter(OrganizationForm);
diff --git a/src/pages/Organizations/screens/Organization/OrganizationEdit.jsx b/src/pages/Organizations/screens/Organization/OrganizationEdit.jsx
index 38d1c255bc..f4de4309d0 100644
--- a/src/pages/Organizations/screens/Organization/OrganizationEdit.jsx
+++ b/src/pages/Organizations/screens/Organization/OrganizationEdit.jsx
@@ -1,82 +1,30 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withRouter } from 'react-router-dom';
-import { Formik, Field } from 'formik';
-import { I18n, i18nMark } from '@lingui/react';
-import { t } from '@lingui/macro';
-import {
- CardBody,
- Form,
- FormGroup,
-} from '@patternfly/react-core';
+import { CardBody } from '@patternfly/react-core';
-import { ConfigContext } from '../../../../context';
-import FormField from '../../../../components/FormField';
-import FormActionGroup from '../../../../components/FormActionGroup';
-import AnsibleSelect from '../../../../components/AnsibleSelect';
-import InstanceGroupsLookup from '../../components/InstanceGroupsLookup';
-
-function required (message) {
- return value => {
- if (!value.trim()) {
- return message || i18nMark('This field must not be blank');
- }
- return undefined;
- };
-}
+import OrganizationForm from '../../components/OrganizationForm';
class OrganizationEdit extends Component {
constructor (props) {
super(props);
- this.getRelatedInstanceGroups = this.getRelatedInstanceGroups.bind(this);
- this.handleInstanceGroupsChange = this.handleInstanceGroupsChange.bind(this);
+ // this.getRelatedInstanceGroups = this.getRelatedInstanceGroups.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.postInstanceGroups = this.postInstanceGroups.bind(this);
this.handleCancel = this.handleCancel.bind(this);
this.handleSuccess = this.handleSuccess.bind(this);
this.state = {
- initialInstanceGroups: [],
- instanceGroups: [],
error: '',
- formIsValid: true,
};
}
- async componentDidMount () {
- let instanceGroups;
- try {
- instanceGroups = await this.getRelatedInstanceGroups();
- } catch (err) {
- this.setState({ error: err });
- }
-
- this.setState({
- instanceGroups,
- initialInstanceGroups: instanceGroups,
- });
- }
-
- async getRelatedInstanceGroups () {
- const {
- api,
- organization: { id }
- } = this.props;
- const { data } = await api.getOrganizationInstanceGroups(id);
- return data.results;
- }
-
- handleInstanceGroupsChange (instanceGroups) {
- this.setState({ instanceGroups });
- }
-
- async handleSubmit (values) {
+ async handleSubmit (values, groupsToAssociate, groupsToDisassociate) {
const { api, organization } = this.props;
- const { instanceGroups } = this.state;
try {
await api.updateOrganizationDetails(organization.id, values);
- await this.postInstanceGroups(instanceGroups);
+ await this.postInstanceGroups(groupsToAssociate, groupsToDisassociate);
} catch (err) {
this.setState({ error: err });
} finally {
@@ -94,19 +42,10 @@ class OrganizationEdit extends Component {
history.push(`/organizations/${id}`);
}
- async postInstanceGroups (instanceGroups) {
+ async postInstanceGroups (groupsToAssociate, groupsToDisassociate) {
const { api, organization } = this.props;
- const { initialInstanceGroups } = this.state;
const url = organization.related.instance_groups;
- const initialIds = initialInstanceGroups.map(ig => ig.id);
- const updatedIds = instanceGroups.map(ig => ig.id);
-
- const groupsToAssociate = [...updatedIds]
- .filter(x => !initialIds.includes(x));
- const groupsToDisassociate = [...initialIds]
- .filter(x => !updatedIds.includes(x));
-
try {
await Promise.all(groupsToAssociate.map(async id => {
await api.associateInstanceGroup(url, id);
@@ -121,91 +60,27 @@ class OrganizationEdit extends Component {
render () {
const { api, organization } = this.props;
- const {
- instanceGroups,
- formIsValid,
- error,
- } = this.state;
- const defaultVenv = '/venv/ansible/';
+ const { error } = this.state;
return (
-
- {({ i18n }) => (
- (
-
- )}
- />
- )}
-
+
+ {error ? error
: null}
);
}
}
+OrganizationEdit.propTypes = {
+ api: PropTypes.shape().isRequired,
+ organization: PropTypes.shape().isRequired,
+};
+
OrganizationEdit.contextTypes = {
custom_virtualenvs: PropTypes.arrayOf(PropTypes.string)
};
diff --git a/src/pages/Organizations/screens/OrganizationAdd.jsx b/src/pages/Organizations/screens/OrganizationAdd.jsx
index f00e118b1c..ec69bbf9be 100644
--- a/src/pages/Organizations/screens/OrganizationAdd.jsx
+++ b/src/pages/Organizations/screens/OrganizationAdd.jsx
@@ -1,26 +1,19 @@
-import React, { Fragment } from 'react';
+import React from 'react';
import PropTypes from 'prop-types';
import { withRouter } from 'react-router-dom';
import { I18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
PageSection,
- Form,
- FormGroup,
- TextInput,
- Gallery,
Card,
CardHeader,
CardBody,
Button,
Tooltip,
} from '@patternfly/react-core';
-import { QuestionCircleIcon, TimesIcon } from '@patternfly/react-icons';
+import { TimesIcon } from '@patternfly/react-icons';
-import { ConfigContext } from '../../../context';
-import AnsibleSelect from '../../../components/AnsibleSelect';
-import FormActionGroup from '../../../components/FormActionGroup';
-import InstanceGroupsLookup from '../components/InstanceGroupsLookup';
+import OrganizationForm from '../components/OrganizationForm';
class OrganizationAdd extends React.Component {
constructor (props) {
@@ -33,12 +26,7 @@ class OrganizationAdd extends React.Component {
this.handleSuccess = this.handleSuccess.bind(this);
this.state = {
- name: '',
- description: '',
- custom_virtualenv: '',
- instanceGroups: [],
error: '',
- defaultEnv: '/venv/ansible/',
};
}
@@ -50,23 +38,15 @@ class OrganizationAdd extends React.Component {
this.setState({ [targetName]: val });
}
- async handleSubmit () {
+ async handleSubmit (values, groupsToAssociate) {
const { api } = this.props;
- const { name, description, custom_virtualenv, instanceGroups } = this.state;
- const data = {
- name,
- description,
- custom_virtualenv
- };
try {
- const { data: response } = await api.createOrganization(data);
+ const { data: response } = await api.createOrganization(values);
const instanceGroupsUrl = response.related.instance_groups;
try {
- if (instanceGroups.length > 0) {
- instanceGroups.forEach(async (select) => {
- await api.associateInstanceGroup(instanceGroupsUrl, select.id);
- });
- }
+ await Promise.all(groupsToAssociate.map(async id => {
+ await api.associateInstanceGroup(instanceGroupsUrl, id);
+ }));
} catch (err) {
this.setState({ error: err });
} finally {
@@ -89,15 +69,7 @@ class OrganizationAdd extends React.Component {
render () {
const { api } = this.props;
- const {
- name,
- description,
- custom_virtualenv,
- defaultEnv,
- instanceGroups,
- error
- } = this.state;
- const enabled = name.length > 0; // TODO: add better form validation
+ const { error } = this.state;
return (
@@ -119,72 +91,12 @@ class OrganizationAdd extends React.Component {
-
+
+ {error ? error
: ''}
)}
@@ -194,6 +106,10 @@ class OrganizationAdd extends React.Component {
}
}
+OrganizationAdd.propTypes = {
+ api: PropTypes.shape().isRequired,
+};
+
OrganizationAdd.contextTypes = {
custom_virtualenvs: PropTypes.arrayOf(PropTypes.string)
};
diff --git a/src/util/validators.js b/src/util/validators.js
new file mode 100644
index 0000000000..4e0f95e25b
--- /dev/null
+++ b/src/util/validators.js
@@ -0,0 +1,19 @@
+import { i18nMark } from '@lingui/react';
+
+export function required (message) {
+ return value => {
+ if (!value.trim()) {
+ return message || i18nMark('This field must not be blank');
+ }
+ return undefined;
+ };
+}
+
+export function maxLength (max) {
+ return value => {
+ if (value.trim() > max) {
+ return i18nMark(`This field must not exceed ${max} characters`);
+ }
+ return undefined;
+ };
+}