|
|
@@ -20,15 +20,17 @@ import React, { PropTypes } from 'react';
|
|
|
import withStyles from 'isomorphic-style-loader/lib/withStyles';
|
|
|
import s from './AddCloudConnection.scss';
|
|
|
import Reflux from 'reflux';
|
|
|
+import Helper from "../Helper"
|
|
|
import ConnectionsStore from '../../stores/ConnectionsStore';
|
|
|
import ConnectionsActions from '../../actions/ConnectionsActions';
|
|
|
import NotificationActions from '../../actions/NotificationActions';
|
|
|
import Dropdown from '../NewDropdown';
|
|
|
import Switch from '../Switch'
|
|
|
import LoadingIcon from "../LoadingIcon/LoadingIcon";
|
|
|
-import ValidateEndpoint from '../ValidateEndpoint';
|
|
|
|
|
|
-const title = 'Add Cloud Endpoint';
|
|
|
+const title = 'Add Cloud Connection';
|
|
|
+const endpointStatuses = { IDLE: 0, VALIDATING: 1, ERROR: 2, SUCCESS: 3 }
|
|
|
+const submissionTypes = { ADD: 0, EDIT: 1 }
|
|
|
|
|
|
class AddCloudConnection extends Reflux.Component {
|
|
|
|
|
|
@@ -49,13 +51,15 @@ class AddCloudConnection extends Reflux.Component {
|
|
|
this.store = ConnectionsStore
|
|
|
|
|
|
this.state = {
|
|
|
+ submissionType: submissionTypes.ADD,
|
|
|
+ endpointStatus: endpointStatuses.IDLE,
|
|
|
+ showErrorMessage: false,
|
|
|
type: props.type, // type of operation: new/edit
|
|
|
connection: props.connection, // connection object (on edit)
|
|
|
connectionName: "", // connection name field
|
|
|
description: "", // connection description field
|
|
|
currentCloud: this.props.cloud, // chosen cloud - if adding a new endpoint
|
|
|
currentCloudData: null, // endpoint field data
|
|
|
- validateEndpoint: false, // holds the endpoint object when validation
|
|
|
requiredFields: [], // array that holds all the endpoint required fields - used for field validation
|
|
|
cloudFormsSubmitted: false // flag that indicates if the form has been submitted - used for field validation
|
|
|
}
|
|
|
@@ -63,10 +67,19 @@ class AddCloudConnection extends Reflux.Component {
|
|
|
|
|
|
componentWillMount() {
|
|
|
super.componentWillMount.call(this)
|
|
|
+ this.componentWillUnmount = false
|
|
|
this.context.onSetTitle(title);
|
|
|
if (this.state.currentCloudData == null) {
|
|
|
this.setState({ currentCloudData: {} })
|
|
|
}
|
|
|
+
|
|
|
+ this.setState({ submissionType: this.props.type === 'new' ? submissionTypes.ADD : submissionTypes.EDIT })
|
|
|
+ }
|
|
|
+
|
|
|
+ componentWillUnmount() {
|
|
|
+ super.componentWillUnmount.call(this)
|
|
|
+ this.componentWillUnmount = true
|
|
|
+ clearTimeout(this.closeTimeout)
|
|
|
}
|
|
|
|
|
|
componentDidMount() {
|
|
|
@@ -186,40 +199,64 @@ class AddCloudConnection extends Reflux.Component {
|
|
|
})
|
|
|
|
|
|
// If endpoint is new
|
|
|
- if (this.state.type == "new") {
|
|
|
+ if (this.state.type === 'new') {
|
|
|
+ this.setState({ type: 'edit' })
|
|
|
+
|
|
|
ConnectionsActions.newEndpoint({
|
|
|
name: this.state.connectionName,
|
|
|
description: this.state.description,
|
|
|
type: this.state.currentCloud.name,
|
|
|
connection_info: credentials
|
|
|
}, (response) => {
|
|
|
- this.setState({
|
|
|
- validateEndpoint: response.data.endpoint,
|
|
|
- type: "edit",
|
|
|
- connection: response.data.endpoint
|
|
|
- })
|
|
|
+ this.validateEndpoint(response.data.endpoint)
|
|
|
+
|
|
|
+ if (this.props.onConnectionAdded) {
|
|
|
+ this.props.onConnectionAdded(response.data.endpoint)
|
|
|
+ }
|
|
|
})
|
|
|
- this.props.addHandle(this.state.connectionName);
|
|
|
+
|
|
|
+ this.setState({ endpointStatus: endpointStatuses.VALIDATING })
|
|
|
} else { // If editing an endpoint
|
|
|
ConnectionsActions.editEndpoint(this.state.connection, {
|
|
|
name: this.state.connectionName,
|
|
|
description: this.state.description,
|
|
|
connection_info: credentials
|
|
|
}, (response) => {
|
|
|
- this.setState({
|
|
|
- validateEndpoint: response.data.endpoint,
|
|
|
- type: "edit",
|
|
|
- connection: response.data.endpoint
|
|
|
- })
|
|
|
- this.props.updateHandle({
|
|
|
- name: this.state.connectionName,
|
|
|
- description: this.state.description
|
|
|
- })
|
|
|
+ this.validateEndpoint(response.data.endpoint)
|
|
|
+
|
|
|
+ if (this.props.onConnectionAdded && this.submissionType === submissionTypes.ADD) {
|
|
|
+ this.props.onConnectionAdded(response.data.endpoint)
|
|
|
+ }
|
|
|
})
|
|
|
+
|
|
|
+ this.setState({ endpointStatus: endpointStatuses.VALIDATING })
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ validateEndpoint(endpoint) {
|
|
|
+ if (this.componentWillUnmount && this.state.submissionType === submissionTypes.ADD) {
|
|
|
+ ConnectionsActions.deleteConnection(endpoint)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ this.setState({ connection: endpoint })
|
|
|
+
|
|
|
+ ConnectionsActions.validateConnection(endpoint, response => {
|
|
|
+ let validation = response.data["validate-connection"]
|
|
|
+ if (validation.valid) {
|
|
|
+ this.setState({ endpointStatus: endpointStatuses.SUCCESS })
|
|
|
+ } else {
|
|
|
+ this.setState({
|
|
|
+ endpointStatus: endpointStatuses.ERROR,
|
|
|
+ errorMessage: validation.message
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }, () => {
|
|
|
+ this.setState({ endpointStatus: endpointStatuses.ERROR })
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Handles change `name` property
|
|
|
* @param e
|
|
|
@@ -290,6 +327,21 @@ class AddCloudConnection extends Reflux.Component {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Handles cancel edit/add endpoint
|
|
|
+ */
|
|
|
+ handleCancel() {
|
|
|
+ if (this.state.submissionType === submissionTypes.ADD && this.state.connection && this.state.connection.id) {
|
|
|
+ ConnectionsActions.deleteConnection(this.state.connection)
|
|
|
+ }
|
|
|
+
|
|
|
+ this.props.closeHandle();
|
|
|
+ }
|
|
|
+
|
|
|
+ handleClose() {
|
|
|
+ this.props.closeHandle();
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Sets default values for cloud fields
|
|
|
*/
|
|
|
@@ -369,11 +421,9 @@ class AddCloudConnection extends Reflux.Component {
|
|
|
this.setState({ requiredFields: requiredFields });
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * Handles cancel edit/add endpoint
|
|
|
- */
|
|
|
- handleCancel() {
|
|
|
- this.props.closeHandle();
|
|
|
+ areFieldsDisabled() {
|
|
|
+ return (this.state.endpointStatus === endpointStatuses.VALIDATING
|
|
|
+ || this.state.endpointStatus === endpointStatuses.SUCCESS)
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -398,6 +448,22 @@ class AddCloudConnection extends Reflux.Component {
|
|
|
this.setState({ currentCloudData: currentCloudData })
|
|
|
}
|
|
|
|
|
|
+ handleCopyErrorClick() {
|
|
|
+ let succesful = Helper.copyTextToClipboard(this.state.errorMessage)
|
|
|
+
|
|
|
+ if (succesful) {
|
|
|
+ NotificationActions.notify('The error message has been copied to clipboard.')
|
|
|
+ } else {
|
|
|
+ NotificationActions.notify('The error message couldn\'t be copied', 'error')
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ handleShowErrorClick() {
|
|
|
+ this.setState({
|
|
|
+ showErrorMessage: !this.state.showErrorMessage
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Renders the cloud list
|
|
|
* @returns {XML}
|
|
|
@@ -448,8 +514,9 @@ class AddCloudConnection extends Reflux.Component {
|
|
|
<input
|
|
|
type="text"
|
|
|
placeholder={field.label}
|
|
|
+ disabled={this.areFieldsDisabled()}
|
|
|
onChange={(e) => this.handleCloudFieldChange(e, field)}
|
|
|
- value={this.state.currentCloudData[field.name]}
|
|
|
+ value={this.state.currentCloudData[field.name] || ''}
|
|
|
/>
|
|
|
</div>
|
|
|
)
|
|
|
@@ -463,6 +530,7 @@ class AddCloudConnection extends Reflux.Component {
|
|
|
<input
|
|
|
type="password"
|
|
|
placeholder={field.label}
|
|
|
+ disabled={this.areFieldsDisabled()}
|
|
|
onChange={(e) => this.handleCloudFieldChange(e, field)}
|
|
|
value={this.state.currentCloudData[field.name]}
|
|
|
/>
|
|
|
@@ -496,6 +564,7 @@ class AddCloudConnection extends Reflux.Component {
|
|
|
{field.label + (field.required ? " *" : "")}
|
|
|
</div>
|
|
|
<Dropdown
|
|
|
+ disabled={this.areFieldsDisabled()}
|
|
|
options={field.options}
|
|
|
onChange={(e) => this.handleCloudFieldChange(e, field)}
|
|
|
placeholder="Choose a value"
|
|
|
@@ -514,13 +583,14 @@ class AddCloudConnection extends Reflux.Component {
|
|
|
})
|
|
|
let radioOptions = field.options.map((option, key) => (
|
|
|
<div key={"radio_option_" + key} className={s.radioOption}>
|
|
|
- <input
|
|
|
- type="radio"
|
|
|
- value={option.value}
|
|
|
- id={option.name}
|
|
|
- checked={option.value == this.state.currentCloudData[field.name]}
|
|
|
- onChange={(e) => this.handleCloudFieldChange(e, field)}
|
|
|
- /> <label htmlFor={option.name}>{option.label}</label>
|
|
|
+ <input
|
|
|
+ disabled={this.areFieldsDisabled()}
|
|
|
+ type="radio"
|
|
|
+ value={option.value}
|
|
|
+ id={option.name}
|
|
|
+ checked={option.value == this.state.currentCloudData[field.name]}
|
|
|
+ onChange={(e) => this.handleCloudFieldChange(e, field)}
|
|
|
+ /> <label htmlFor={option.name}>{option.label}</label>
|
|
|
</div>
|
|
|
)
|
|
|
)
|
|
|
@@ -542,6 +612,108 @@ class AddCloudConnection extends Reflux.Component {
|
|
|
return returnValue
|
|
|
}
|
|
|
|
|
|
+ renderEndpointErrorMessage() {
|
|
|
+ if (this.state.endpointStatus !== endpointStatuses.ERROR || !this.state.showErrorMessage) {
|
|
|
+ return null
|
|
|
+ }
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div className={s.endpointErrorMessage}
|
|
|
+ onClick={() => this.handleCopyErrorClick()}
|
|
|
+ onMouseDown={e => e.stopPropagation()}
|
|
|
+ onMouseUp={e => e.stopPropagation()}
|
|
|
+ >
|
|
|
+ <span className={s.endpointErrorMessageContent}>
|
|
|
+ {this.state.errorMessage}<span className="copyButton" />
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ renderEndpointErrorTitle() {
|
|
|
+ let errorMessage = null
|
|
|
+ if (this.state.errorMessage) {
|
|
|
+ errorMessage = (
|
|
|
+ <span className={s.ednpointErrorMessageViewMore}
|
|
|
+ onClick={() => { this.handleShowErrorClick() }}
|
|
|
+ >{this.state.showErrorMessage ? 'Hide Error' : 'Show Error'}</span>
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div className={s.endpointErrorMessageTitle}>
|
|
|
+ <span className={s.endpointErrorMessageTitleContent}>Validation Failed</span>
|
|
|
+ {errorMessage}
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ renderEndpointStatus() {
|
|
|
+ if (this.state.endpointStatus === endpointStatuses.SUCCESS) {
|
|
|
+ clearTimeout(this.closeTimeout)
|
|
|
+ this.closeTimeout = setTimeout(() => {
|
|
|
+ this.closeTimeout = null
|
|
|
+ this.handleClose()
|
|
|
+ }, 2000)
|
|
|
+ }
|
|
|
+
|
|
|
+ let endpointStatus = null
|
|
|
+ if (this.state.endpointStatus === endpointStatuses.ERROR ||
|
|
|
+ this.state.endpointStatus === endpointStatuses.SUCCESS) {
|
|
|
+ let icon = 'successIcon'
|
|
|
+ let content = 'Endpoint is Valid'
|
|
|
+ if (this.state.endpointStatus === endpointStatuses.ERROR) {
|
|
|
+ icon = 'errorIcon'
|
|
|
+ content = this.renderEndpointErrorTitle()
|
|
|
+ }
|
|
|
+
|
|
|
+ endpointStatus = (
|
|
|
+ <div className={s.endpointStatus}>
|
|
|
+ <div className={s.endpointStatusTitle}>
|
|
|
+ <div className={s.endpointStatusIcon + ' ' + icon}></div>
|
|
|
+ <div className={s.endpointStatusLabel}>
|
|
|
+ {content}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ {this.renderEndpointErrorMessage()}
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ return endpointStatus
|
|
|
+ }
|
|
|
+
|
|
|
+ renderButtons() {
|
|
|
+ let cancelButton = (this.state.type == "new" && this.props.cloud == null) ?
|
|
|
+ <button className={s.leftBtn + " gray"} onClick={(e) => this.handleBack(e)}>Back</button> :
|
|
|
+ <button className={s.leftBtn + " gray"} onClick={(e) => this.handleCancel(e)}>Cancel</button>
|
|
|
+
|
|
|
+ let saveButtonContent = 'Validate and Save'
|
|
|
+
|
|
|
+ if (this.state.endpointStatus === endpointStatuses.VALIDATING ||
|
|
|
+ this.state.endpointStatus === endpointStatuses.SUCCESS) {
|
|
|
+ let text = this.state.endpointStatus === endpointStatuses.VALIDATING ? 'Validating' : 'Saving'
|
|
|
+ saveButtonContent = <span>{text} ... <div className="spinner"></div></span>
|
|
|
+ }
|
|
|
+
|
|
|
+ let saveButton = (
|
|
|
+ <button
|
|
|
+ className={s.rightBtn}
|
|
|
+ onClick={this.handleSave.bind(this)}
|
|
|
+ disabled={this.areFieldsDisabled()}
|
|
|
+ >
|
|
|
+ {saveButtonContent}
|
|
|
+ </button>
|
|
|
+ )
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div className={s.buttons}>
|
|
|
+ {cancelButton}
|
|
|
+ {saveButton}
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Renders the new/edit endpoint form
|
|
|
* @param cloud
|
|
|
@@ -555,6 +727,7 @@ class AddCloudConnection extends Reflux.Component {
|
|
|
<div className={s.cloudImage}>
|
|
|
<div className={" icon large-cloud " + this.state.currentCloud.name}></div>
|
|
|
</div>
|
|
|
+ {this.renderEndpointStatus()}
|
|
|
<div className={s.cloudFields + (cloud.endpoint.fields.length > 6 ? " " + s.larger : "")}>
|
|
|
<div className={"form-group " + (this.state.cloudFormsSubmitted &&
|
|
|
this.state.connectionName.trim().length == 0 ? s.error : "") + ' required'}
|
|
|
@@ -565,6 +738,7 @@ class AddCloudConnection extends Reflux.Component {
|
|
|
<input
|
|
|
type="text"
|
|
|
placeholder="Endpoint Name"
|
|
|
+ disabled={this.areFieldsDisabled()}
|
|
|
onChange={(e) => this.handleChangeName(e)}
|
|
|
value={this.state.connectionName}
|
|
|
/>
|
|
|
@@ -575,6 +749,7 @@ class AddCloudConnection extends Reflux.Component {
|
|
|
</div>
|
|
|
<input
|
|
|
type="text"
|
|
|
+ disabled={this.areFieldsDisabled()}
|
|
|
placeholder="Endpoint Description"
|
|
|
onChange={(e) => this.handleChangeDescription(e)}
|
|
|
value={this.state.description}
|
|
|
@@ -583,29 +758,14 @@ class AddCloudConnection extends Reflux.Component {
|
|
|
|
|
|
{fields}
|
|
|
</div>
|
|
|
- <div className={s.buttons}>
|
|
|
- {(this.state.type == "new" && this.props.cloud == null) ? (
|
|
|
- <button className={s.leftBtn + " gray"} onClick={(e) => this.handleBack(e)}>Back</button>
|
|
|
- ) : (
|
|
|
- <button className={s.leftBtn + " gray"} onClick={(e) => this.handleCancel(e)}>Cancel</button>
|
|
|
- )}
|
|
|
- <button className={s.rightBtn} onClick={(e) => this.handleSave(e)}>Save</button>
|
|
|
- </div>
|
|
|
+ {this.renderButtons()}
|
|
|
</div>
|
|
|
)
|
|
|
}
|
|
|
|
|
|
render() {
|
|
|
let modalBody
|
|
|
- if (this.state.validateEndpoint) {
|
|
|
- modalBody = (
|
|
|
- <ValidateEndpoint
|
|
|
- closeHandle={this.props.closeHandle}
|
|
|
- endpoint={this.state.validateEndpoint}
|
|
|
- backHandle={(e) => this.backToEdit(e)}
|
|
|
- />
|
|
|
- )
|
|
|
- } else if (this.state.currentCloud == null) {
|
|
|
+ if (this.state.currentCloud == null) {
|
|
|
if (this.state.allClouds) {
|
|
|
modalBody = this.renderCloudList()
|
|
|
} else {
|
|
|
@@ -617,7 +777,7 @@ class AddCloudConnection extends Reflux.Component {
|
|
|
return (
|
|
|
<div tabIndex="0" className={s.root} ref={rootDiv => { this.rootDiv = rootDiv }}>
|
|
|
<div className={s.header}>
|
|
|
- <h3>{title}</h3>
|
|
|
+ <h3>{this.props.type === 'edit' ? 'Edit Cloud Connection' : title}</h3>
|
|
|
</div>
|
|
|
{modalBody}
|
|
|
</div>
|