AddCloudConnection.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. /*
  2. Copyright (C) 2017 Cloudbase Solutions SRL
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. */
  14. import React, { PropTypes } from 'react';
  15. import withStyles from 'isomorphic-style-loader/lib/withStyles';
  16. import s from './AddCloudConnection.scss';
  17. import Reflux from 'reflux';
  18. import ConnectionsStore from '../../stores/ConnectionsStore';
  19. import ConnectionsActions from '../../actions/ConnectionsActions';
  20. import NotificationActions from '../../actions/NotificationActions';
  21. import Dropdown from '../NewDropdown';
  22. import LoadingIcon from "../LoadingIcon/LoadingIcon";
  23. import ValidateEndpoint from '../ValidateEndpoint';
  24. const title = 'Add Cloud Endpoint';
  25. class AddCloudConnection extends Reflux.Component {
  26. static contextTypes = {
  27. onSetTitle: PropTypes.func.isRequired,
  28. };
  29. static defaultProps = {
  30. cloud: null,
  31. connection: null,
  32. type: "new"
  33. }
  34. constructor(props) {
  35. super(props)
  36. this.store = ConnectionsStore
  37. this.state = {
  38. type: props.type,
  39. connection: props.connection,
  40. connectionName: "",
  41. description: null,
  42. currentCloud: this.props.cloud,
  43. currentCloudData: null,
  44. validateEndpoint: false,
  45. isConnecting: false,
  46. requiredFields: [],
  47. cloudFormsSubmitted: false
  48. }
  49. }
  50. componentWillMount() {
  51. super.componentWillMount.call(this)
  52. this.context.onSetTitle(title);
  53. }
  54. componentDidMount() {
  55. if (this.state.connection) {
  56. this.state.allClouds.forEach(item => {
  57. if (item.name === this.state.connection.type) {
  58. let credentials = this.state.connection.credentials
  59. for (let i in credentials) {
  60. credentials[i] = credentials[i] + ""
  61. }
  62. this.setState({
  63. currentCloudData: credentials,
  64. connectionName: this.state.connection.name,
  65. description: this.state.connection.description
  66. }, () => {
  67. this.chooseCloud(item)
  68. })
  69. }
  70. })
  71. } else if (this.props.cloud) {
  72. this.chooseCloud(this.props.cloud)
  73. }
  74. }
  75. handleChangeName(e) {
  76. this.setState({ connectionName: e.target.value })
  77. }
  78. handleChangeDescription(e) {
  79. this.setState({ description: e.target.value })
  80. }
  81. handleSave() {
  82. let valid = true
  83. for (let i in this.state.currentCloudData) {
  84. if (this.state.requiredFields.indexOf(i) > -1 && !this.state.currentCloudData[i]) {
  85. valid = false
  86. }
  87. }
  88. if (this.state.connectionName.trim().length == 0) {
  89. valid = false
  90. }
  91. if (!valid) {
  92. NotificationActions.notify("Please fill all required fields", "error")
  93. this.setState({ cloudFormsSubmitted: true })
  94. } else {
  95. let credentials = Object.assign({}, this.state.currentCloudData)
  96. for (let key in credentials) {
  97. if (credentials[key].label) {
  98. credentials[key] = credentials[key].value
  99. }
  100. }
  101. if (this.state.type == "new") {
  102. ConnectionsActions.newEndpoint({
  103. name: this.state.connectionName,
  104. description: this.state.description,
  105. type: this.state.currentCloud.name,
  106. connection_info: credentials
  107. }, (response) => {
  108. this.setState({
  109. validateEndpoint: response.data.endpoint,
  110. type: "edit",
  111. connection: response.data.endpoint
  112. })
  113. })
  114. this.props.addHandle(this.state.connectionName);
  115. } else {
  116. ConnectionsActions.editEndpoint(this.state.connection, {
  117. name: this.state.connectionName,
  118. description: this.state.description,
  119. connection_info: credentials
  120. }, () => {
  121. this.props.updateHandle({
  122. name: this.state.connectionName,
  123. description: this.state.description
  124. })
  125. })
  126. this.props.closeHandle()
  127. }
  128. }
  129. }
  130. chooseCloud(cloud) {
  131. let currentCloudData = {}
  132. if (this.state.currentCloudData !== null) {
  133. currentCloudData = this.state.currentCloudData
  134. }
  135. let requiredFields = []
  136. cloud.endpoint.fields.forEach(field => {
  137. if (typeof currentCloudData[field.name] == "undefined") {
  138. if (field.type == "dropdown") {
  139. currentCloudData[field.name] = field.options[0]
  140. } else {
  141. currentCloudData[field.name] = ""
  142. }
  143. }
  144. if (field.required) {
  145. requiredFields.push(field.name)
  146. }
  147. })
  148. this.setState({
  149. currentCloud: cloud,
  150. currentCloudData: currentCloudData,
  151. requiredFields: requiredFields
  152. }, this.setDefaultValues)
  153. }
  154. backToEdit() {
  155. this.setState({ validateEndpoint: null })
  156. }
  157. handleBack() {
  158. this.setState({
  159. currentCloudData: null,
  160. currentCloud: null,
  161. requiredFields: null,
  162. connectionName: "",
  163. description: null
  164. })
  165. }
  166. setDefaultValues() {
  167. this.state.currentCloud.endpoint.fields.forEach(field => {
  168. let currentCloudData = this.state.currentCloudData
  169. switch (field.type) {
  170. case 'dropdown':
  171. field.options.forEach(option => {
  172. if (option.default === true && typeof currentCloudData[field.name] == "undefined") {
  173. currentCloudData[field.name] = option
  174. this.setState({ currentCloudData: currentCloudData })
  175. }
  176. }, this)
  177. break
  178. case 'switch-radio':
  179. field.options.forEach(option => {
  180. if (option.default && typeof currentCloudData[field.name] == "undefined") {
  181. currentCloudData[field.name] = option.value
  182. this.setState({ currentCloudData: currentCloudData })
  183. }
  184. }, this)
  185. break;
  186. case 'text':
  187. if (field.default && typeof currentCloudData[field.name] == "undefined") {
  188. currentCloudData[field.name] = field.default
  189. this.setState({ currentCloudData: currentCloudData })
  190. }
  191. break
  192. default:
  193. break;
  194. }
  195. }, this)
  196. }
  197. isValid(field) {
  198. if (field.required && this.state.cloudFormsSubmitted) {
  199. if (this.state.currentCloudData[field.name].length == 0) {
  200. return false
  201. } else {
  202. return true
  203. }
  204. } else {
  205. return true
  206. }
  207. }
  208. handleCancel() {
  209. this.props.closeHandle();
  210. }
  211. renderCloudList() {
  212. let clouds = this.state.allClouds.map((cloud, index) => {
  213. let colorType = ""
  214. if (cloud.credentials != null && cloud.credentials.length != 0) {
  215. colorType = ""
  216. }
  217. return (
  218. <div className={s.cloudContainer} key={"cloudImage_" + index}>
  219. <div
  220. className={s.cloudImage + " icon large-cloud " + cloud.name + " " + colorType}
  221. onClick={() => this.chooseCloud(cloud)}
  222. ></div>
  223. </div>
  224. )
  225. }, this)
  226. return (
  227. <div className={s.container}>
  228. <div className={s.cloudList}>
  229. {clouds}
  230. </div>
  231. <div className={s.buttons}>
  232. <button className={s.centerBtn + " gray"} onClick={(e) => this.handleCancel(e)}>Cancel</button>
  233. </div>
  234. </div>
  235. )
  236. }
  237. renderField(field) {
  238. let returnValue
  239. switch (field.type) {
  240. case "text":
  241. returnValue = (
  242. <div className={"form-group " + (this.isValid(field) ? "" : s.error)} key={"cloudField_" + field.name}>
  243. <input
  244. type="text"
  245. placeholder={field.label + (field.required ? " *" : "")}
  246. onChange={(e) => this.handleCloudFieldChange(e, field)}
  247. value={this.state.currentCloudData[field.name]}
  248. />
  249. </div>
  250. )
  251. break;
  252. case "password":
  253. returnValue = (
  254. <div className={"form-group " + (this.isValid(field) ? "" : s.error)} key={"cloudField_" + field.name}>
  255. <input
  256. type="password"
  257. placeholder={field.label + (field.required ? " *" : "")}
  258. onChange={(e) => this.handleCloudFieldChange(e, field)}
  259. value={this.state.currentCloudData[field.name]}
  260. />
  261. </div>
  262. )
  263. break;
  264. case "dropdown":
  265. returnValue = (
  266. <div className={"form-group " + (this.isValid(field) ? "" : s.error)} key={"cloudField_" + field.name}>
  267. <Dropdown
  268. options={field.options}
  269. onChange={(e) => this.handleCloudFieldChange(e, field)}
  270. placeholder={field.label + (field.required ? " *" : "")}
  271. value={this.state.currentCloudData[field.name]}
  272. />
  273. </div>
  274. )
  275. break;
  276. case "switch-radio":
  277. let fields = ""
  278. field.options.forEach((option) => {
  279. if (option.value == this.state.currentCloudData[field.name]) {
  280. fields = option.fields.map((optionField) => this.renderField(optionField))
  281. }
  282. })
  283. let radioOptions = field.options.map((option, key) => (
  284. <div key={"radio_option_" + key} className={s.radioOption}>
  285. <input
  286. type="radio"
  287. value={option.value}
  288. id={option.name}
  289. checked={option.value == this.state.currentCloudData[field.name]}
  290. onChange={(e) => this.handleCloudFieldChange(e, field)}
  291. /> <label htmlFor={option.name}>{option.label}</label>
  292. </div>
  293. )
  294. )
  295. returnValue = (
  296. <div key={"cloudField_" + field.name}>
  297. <div className="form-group switch-radio" key={"cloudField_" + field.name}>
  298. { radioOptions }
  299. </div>
  300. <div></div>
  301. {fields}
  302. </div>
  303. )
  304. break;
  305. default:
  306. break
  307. }
  308. return returnValue
  309. }
  310. handleCloudFieldChange(e, field) {
  311. let currentCloudData = this.state.currentCloudData
  312. if (field.type == 'dropdown') {
  313. currentCloudData[field.name] = e
  314. } else {
  315. currentCloudData[field.name] = e.target.value
  316. }
  317. this.setState({ currentCloudData: currentCloudData })
  318. }
  319. renderCloudFields(cloud) {
  320. if (this.state.currentCloudData == null) {
  321. this.setState({ currentCloudData: {} })
  322. }
  323. if (!this.state.isConnecting) {
  324. let fields = cloud.endpoint.fields.map(field => this.renderField(field), this)
  325. return (
  326. <div className={s.container}>
  327. <div className={s.cloudImage}>
  328. <div className={" icon large-cloud " + this.state.currentCloud.name}></div>
  329. </div>
  330. <div className={"form-group " + (this.state.cloudFormsSubmitted &&
  331. this.state.connectionName.trim().length == 0 ? s.error : "")}
  332. >
  333. <input
  334. type="text"
  335. placeholder="Endpoint Name *"
  336. onChange={(e) => this.handleChangeName(e)}
  337. value={this.state.connectionName}
  338. />
  339. </div>
  340. <div className="form-group">
  341. <textarea
  342. placeholder="Endpoint Description"
  343. onChange={(e) => this.handleChangeDescription(e)}
  344. value={this.state.description}
  345. ></textarea>
  346. </div>
  347. <div className={s.cloudFields + (cloud.endpoint.fields.length > 6 ? " " + s.larger : "")}>
  348. {fields}
  349. </div>
  350. <div className={s.buttons}>
  351. {this.state.type == "new" ? (
  352. <button className={s.leftBtn + " gray"} onClick={(e) => this.handleBack(e)}>Back</button>
  353. ) : (
  354. <button className={s.leftBtn + " gray"} onClick={(e) => this.handleCancel(e)}>Cancel</button>
  355. )}
  356. <button className={s.rightBtn} onClick={(e) => this.handleSave(e)}>Save</button>
  357. </div>
  358. </div>
  359. )
  360. } else {
  361. return (
  362. <div className={s.connecting}>
  363. <LoadingIcon />
  364. <div className={s.text}>Connecting ...</div>
  365. </div>)
  366. }
  367. }
  368. renderSaveConnection() {
  369. return (
  370. <div className={s.container}>
  371. <div className={s.cloudImage}>
  372. <div className={" icon large-cloud " + this.state.currentCloud.name}></div>
  373. </div>
  374. <div className="form-group">
  375. <input
  376. type="text"
  377. placeholder="Connection name"
  378. onChange={(e) => this.handleChangeName(e)}
  379. value={this.state.connectionName}
  380. />
  381. </div>
  382. <div className="form-group">
  383. <textarea onChange={(e) => this.handleChangeDescription(e)} value={this.state.description}></textarea>
  384. </div>
  385. <div className={s.buttons}>
  386. <button className={s.leftBtn + " gray"} onClick={(e) => this.handleBack(e)}>Back</button>
  387. <button className={s.rightBtn} onClick={(e) => this.handleSave(e)}>Add</button>
  388. </div>
  389. </div>
  390. )
  391. }
  392. render() {
  393. let modalBody
  394. if (this.state.validateEndpoint) {
  395. modalBody = (
  396. <ValidateEndpoint
  397. closeHandle={this.props.closeHandle}
  398. endpoint={this.state.validateEndpoint}
  399. backHandle={(e) => this.backToEdit(e)}
  400. />
  401. )
  402. } else if (this.state.currentCloud == null) {
  403. if (this.state.allClouds) {
  404. modalBody = this.renderCloudList()
  405. } else {
  406. modalBody = <LoadingIcon />
  407. }
  408. } else {
  409. modalBody = this.renderCloudFields(this.state.currentCloud)
  410. }
  411. return (
  412. <div className={s.root}>
  413. <div className={s.header}>
  414. <h3>{title}</h3>
  415. </div>
  416. {modalBody}
  417. </div>
  418. );
  419. }
  420. }
  421. export default withStyles(AddCloudConnection, s);