3 * Copyright 2016 RIFT.IO Inc
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
19 import React from 'react';
20 import Button from 'widgets/button/rw.button.js';
21 import _cloneDeep from 'lodash/cloneDeep';
22 import SkyquakeComponent from 'widgets/skyquake_container/skyquakeComponent.jsx';
23 import Crouton from 'react-crouton';
24 import TextInput from 'widgets/form_controls/textInput.jsx';
25 import {AccountConnectivityStatus} from '../account_sidebar/accountSidebar.jsx';
27 import 'style/common.scss';
28 import './account.scss';
29 class Account extends React.Component {
33 this.state.account = {};
35 storeListener = (state) => {
37 this.setUp(this.props)
39 state.account && this.setState({account: state.account,accountType: state.accountType, types: state.types, sdnOptions: state.sdnOptions})
41 componentWillMount() {
42 this.props.store.listen(this.storeListener);
43 this.setUp(this.props);
45 componentWillReceiveProps(nextProps) {
46 if(JSON.stringify(nextProps.params) != JSON.stringify(this.props.params)){
47 this.setUp(nextProps);
50 componentWillUnmount() {
51 this.props.store.unlisten(this.storeListener);
54 if(props.params.name != 'create') {
55 this.props.store.viewAccount({type: props.params.type, name: props.params.name});
57 this.props.store.setAccountTemplate(props.params.type);
63 var Account = this.state.account;
64 let AccountType = this.state.accountType;
65 if (Account.name == "") {
66 self.props.flux.actions.global.showNotification("Please give the account a name");
69 var type = Account['account-type'];
70 var params = Account.params;
73 for (var i = 0; i < params.length; i++) {
74 var param = params[i].ref;
75 if (typeof(Account[type]) == 'undefined' || typeof(Account[type][param]) == 'undefined' || Account[type][param] == "") {
76 if (!params[i].optional) {
77 self.props.flux.actions.global.showNotification("Please fill all account details");
84 let nestedParams = Account.nestedParams && Account.nestedParams;
85 if (nestedParams && nestedParams.params) {
86 for (let i = 0; i < nestedParams.params.length; i++) {
87 let nestedParam = nestedParams.params[i].ref;
88 if (typeof(Account[type]) == 'undefined' || typeof(Account[type][nestedParams['container-name']][nestedParam]) == 'undefined' || Account[type][nestedParams['container-name']][nestedParam] == "") {
89 if (!nestedParams.params[i].optional) {
90 self.props.flux.actions.global.showNotification("Please fill all account details");
98 let newAccount = _cloneDeep(removeTrailingWhitespace(Account));
99 delete newAccount.params;
100 newAccount.nestedParams &&
101 newAccount.nestedParams['container-name'] &&
102 delete newAccount[newAccount.nestedParams['container-name']];
103 delete newAccount.nestedParams;
105 this.props.flux.actions.global.showScreenLoader();
106 this.props.store.create(newAccount, AccountType).then(function() {
107 self.props.router.push({pathname:'accounts'});
108 self.props.flux.actions.global.hideScreenLoader.defer();
111 self.props.flux.actions.global.showNotification(error);
112 self.props.flux.actions.global.hideScreenLoader.defer();
118 var Account = this.state.account;
119 let AccountType = this.state.accountType;
120 this.props.flux.actions.global.showScreenLoader();
121 this.props.store.update(Account, AccountType).then(function() {
122 self.props.router.push({pathname:'accounts'});
123 self.props.flux.actions.global.hideScreenLoader();
132 this.props.router.push({pathname:'accounts'});
134 handleDelete = () => {
136 let msg = 'Preparing to delete "' + self.state.account.name + '"' +
137 ' Are you sure you want to delete this ' + self.state.accountType + ' account?"';
138 if (window.confirm(msg)) {
139 this.props.store.delete(self.state.accountType, self.state.account.name).then(function() {
140 self.props.flux.actions.global.hideScreenLoader();
141 self.props.router.push({pathname:'accounts'});
143 // self.props.flux.actions.global.hideScreenLoader.defer();
144 // console.log('Delete Account Fail');
147 self.props.flux.actions.global.hideScreenLoader();
150 handleNameChange(event) {
151 this.props.store.handleNameChange(event);
153 handleAccountTypeChange(node, event) {
154 this.props.store.handleAccountTypeChange(node, event);
156 handleSelectSdnAccount = (e) => {
157 var tmp = this.state.account;
159 tmp['sdn-account'] = e;
161 if(tmp['sdn-account']) {
162 delete tmp['sdn-account'];
167 preventDefault = (e) => {
171 evaluateSubmit = (e) => {
172 if (e.keyCode == 13) {
173 if (this.props.params.name != 'create') {
185 let {store, ...props} = this.props;
186 // This section builds elements that only show up on the create page.
187 // var name = <label>Name <input type="text" onChange={this.handleNameChange.bind(this)} style={{'textAlign':'left'}} /></label>;
188 var name = <TextInput label="Name" onChange={this.handleNameChange.bind(this)} required={true} />;
190 let selectAccount = null;
191 let resfreshStatus = null;
192 let Account = this.state.account;
193 // AccountType is for the view, not the data account-type value;
194 let AccountType = this.state.accountType;
195 let Types = this.state.types;
196 let isEdit = this.props.params.name != 'create';
198 let cloudResources = Account['cloud-resources-state'] && Account['cloud-resources-state'][Account['account-type']];
199 let cloudResourcesStateHTML = null;
201 // Account Type Radio
202 var selectAccountStack = [];
205 <Button key="0" onClick={this.cancel} className="cancel light" label="Cancel"></Button>,
206 <Button key="1" role="button" onClick={this.create.bind(this)} className="save dark" label="Save" />
208 for (var i = 0; i < Types.length; i++) {
210 var isSelected = (Account['account-type'] == node['account-type']);
211 selectAccountStack.push(
212 <label key={i} className={"accountSelection " + (isSelected ? "accountSelection--isSelected" : "")}>
213 <div className={"accountSelection-overlay" + (isSelected ? "accountSelection-overlay--isSelected" : "")}></div>
214 <div className="accountSelection-imageWrapper">
215 <img src={store.getImage(node['account-type'])}/>
217 <input type="radio" name="account" onChange={this.handleAccountTypeChange.bind(this, node)} defaultChecked={node.name == Types[0].name} value={node['account-type']} />{node.name}
222 <div className="accountForm">
223 <h3 className="accountForm-title">Select Account Type</h3>
224 <div className="select-type accountForm-content" >
233 //Cloud Account: SDN Account Option
234 let sdnAccounts = null;
235 if( (AccountType == 'cloud') ) {
236 if ( !isEdit && (this.state.sdnOptions.length > 1 )) {
238 <div className="associateSdnAccount accountForm">
239 <h3 className="accountForm-title">Associate SDN Account</h3>
240 <div className="accountForm-content">
241 <SelectOption options={this.state.sdnOptions} onChange={this.handleSelectSdnAccount} />
247 if(isEdit && Account['sdn-account']) {
248 sdnAccounts = ( <div className="associateSdnAccount">SDN Account: {Account['sdn-account']} </div>)
251 //END Cloud Account: SDN Account Option
253 // This sections builds the parameters for the account details.
254 if (Account.params) {
255 var paramsStack = [];
256 var optionalField = '';
257 for (var i = 0; i < Account.params.length; i++) {
258 var node = Account.params[i];
260 if (Account[Account['account-type']]) {
261 value = Account[Account['account-type']][node.ref]
263 if (this.props.edit && Account.params) {
264 value = Account.params[node.ref];
269 className="accountForm-input"
271 required={!node.optional}
272 onChange={this.props.store.handleParamChange(node)}
274 readonly={self.props.readonly}
280 let nestedParamsStack = null;
281 if (Account.nestedParams) {
282 nestedParamsStack = [];
283 var optionalField = '';
284 for (var i = 0; i < Account.nestedParams.params.length; i++) {
285 var node = Account.nestedParams.params[i];
287 if (Account[Account['account-type']] && Account.nestedParams && Account[Account['account-type']][Account.nestedParams['container-name']] && Account[Account['account-type']][Account.nestedParams['container-name']][node.ref]) {
288 value = Account[Account['account-type']][Account.nestedParams['container-name']][node.ref];
291 optionalField = <span className="optional">Optional</span>;
293 // nestedParamsStack.push(
294 // <label key={node.label}>
295 // <label className="create-fleet-pool-params">{node.label} {optionalField}</label>
296 // <input className="create-fleet-pool-input" type="text" onChange={this.props.store.handleNestedParamChange(Account.nestedParams['container-name'], node)} value={value}/>
299 nestedParamsStack.push(
303 required={!node.optional}
304 className="create-fleet-pool-input"
306 onChange={this.props.store.handleNestedParamChange(Account.nestedParams['container-name'], node)}
308 readonly={self.props.readonly}
315 <li className="create-fleet-pool accountForm">
316 <h3 className="accountForm-title"> {isEdit ? 'Update' : 'Enter'} Account Details</h3>
317 <div className="accountForm-content">
319 <div className="accountForm-nestedParams">
327 <li className="create-fleet-pool accountForm">
328 <h3 className="accountForm-title"> {isEdit ? 'Update' : 'Enter'}</h3>
329 <label style={{'marginLeft':'17px', color:'#888'}}>No Details Required</label>
334 // This section builds elements that only show up in the edit page.
336 name = <label>{Account.name}</label>;
338 <Button key="2" onClick={this.handleDelete} className="light" label="Remove Account" />,
339 <Button key="3" onClick={this.cancel} className="light" label="Cancel" />,
340 <Button key="4" role="button" onClick={this.update.bind(this)} className="update dark" label="Update" />
342 resfreshStatus = Account['connection-status'] ? (
343 <div className="accountForm">
344 <div className="accountForm-title accountForm-title--edit">
347 <div className="accountForm-content" style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center'}}>
348 <div style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center'}}>
349 <AccountConnectivityStatus status={Account['connection-status'].status} />
350 {Account['connection-status'] && Account['connection-status'].status && Account['connection-status'].status.toUpperCase()}
352 <Button is-disabled={self.props.readonly} className="refreshList light" onClick={this.props.store.refreshAccount.bind(this, Account.name, AccountType)} label="REFRESH STATUS"></Button>
355 (Account['connection-status'] && Account['connection-status'].status && Account['connection-status'].status.toUpperCase()) === 'FAILURE' ?
356 displayFailureMessage(Account['connection-status'].details) : null
360 // cloudResourcesStateHTML = (
361 // <div className="accountForm">
362 // <h3 className="accountForm-title">Resources Status</h3>
363 // <div className="accountForm-content" >
366 // cloudResources && props.AccountMeta.resources[Account['account-type']].map(function(r, i) {
370 // {r}: {cloudResources[r]}
373 // }) || 'No Additional Resources'
383 <form className="app-body create Accounts" onSubmit={this.preventDefault} onKeyDown={this.evaluateSubmit}>
384 <div className="noticeSubText noticeSubText_right">
387 <div className="associateSdnAccount accountForm">
388 <h3 className="accountForm-title">Account</h3>
389 <div className="accountForm-content" style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center'}}>
390 <h4 style={{flex: '1'}}>{name}</h4>
393 <div style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center'}}>
394 <img src={store.getImage(Account['account-type'])}/> {props.AccountMeta.labelByType[Account['account-type']]}
404 {cloudResourcesStateHTML}
405 <ol className="flex-row">
408 <div className="form-actions">
409 {!self.props.readonly ? buttons : null}
417 Account.contextTypes = {
418 router: React.PropTypes.object,
419 userProfile: React.PropTypes.object
422 function displayFailureMessage(msg) {
424 <div className="accountForm-content" style={{maxWidth: '600px'}}>
425 <div style={{paddingBottom: '1rem'}}>Details:</div>
434 class SelectOption extends React.Component {
438 handleOnChange = (e) => {
439 this.props.onChange(JSON.parse(e.target.value));
444 <select className={this.props.className} onChange={this.handleOnChange}>
446 this.props.options.map(function(op, i) {
447 return <option key={i} value={JSON.stringify(op.value)}>{op.label}</option>
455 SelectOption.defaultProps = {
457 onChange: function(e) {
462 function removeTrailingWhitespace(Account) {
463 var type = Account['account-type'];
464 var params = Account.params;
467 for (var i = 0; i < params.length; i++) {
468 var param = params[i].ref;
469 if(typeof(Account[type][param]) == 'string') {
470 Account[type][param] = Account[type][param].trim();
475 let nestedParams = Account.nestedParams;
476 if (nestedParams && nestedParams.params) {
477 for (let i = 0; i < nestedParams.params.length; i++) {
478 let nestedParam = nestedParams.params[i].ref;
479 let nestedParamValue = Account[type][nestedParams['container-name']][nestedParam];
480 if (typeof(nestedParamValue) == 'string') {
481 Account[type][nestedParams['container-name']][nestedParam] = nestedParamValue.trim();
488 export default SkyquakeComponent(Account)