RIFT-15726 - optimize download size -> lodash usage in UI
[osm/UI.git] / skyquake / plugins / config / src / dashboard / inputs.jsx
1 /*
2  * STANDARD_RIFT_IO_COPYRIGHT
3  */
4
5 import React from 'react';
6 import Button from 'widgets/button/rw.button.js';
7 import _cloneDeep from 'lodash/cloneDeep';
8 import SkyquakeComponent from 'widgets/skyquake_container/skyquakeComponent.jsx';
9 import Crouton from 'react-crouton';
10 import TextInput from 'widgets/form_controls/textInput.jsx';
11 import 'style/common.scss';
12 import './config.scss';
13 class Account extends React.Component {
14     constructor(props) {
15         super(props);
16         this.state = {};
17         this.state.account = {};
18     }
19     storeListener = (state) => {
20         if(!state.account) {
21             this.setUp(this.props)
22         }
23         state.account && this.setState({account: state.account,accountType: state.accountType, types: state.types, sdnOptions: state.sdnOptions})
24     }
25     componentWillMount() {
26         this.props.store.listen(this.storeListener);
27         this.setUp(this.props);
28     }
29     componentWillReceiveProps(nextProps) {
30         if(JSON.stringify(nextProps.params) != JSON.stringify(this.props.params)){
31               this.setUp(nextProps);
32         }
33     }
34     componentWillUnmount() {
35         this.props.store.unlisten(this.storeListener);
36     }
37     setUp(props){
38         if(props.params.name != 'create') {
39             this.props.store.viewAccount({type: props.params.type, name: props.params.name});
40         } else {
41             this.props.store.setAccountTemplate(props.params.type);
42         }
43     }
44     create(e) {
45         e.preventDefault();
46         var self = this;
47         var Account = this.state.account;
48         let AccountType = this.state.accountType;
49         if (Account.name == "") {
50             self.props.flux.actions.global.showNotification("Please give the account a name");
51             return;
52         } else {
53             var type = Account['account-type'];
54             var params = Account.params;
55
56             if(params) {
57                 for (var i = 0; i < params.length; i++) {
58                     var param = params[i].ref;
59                     if (typeof(Account[type]) == 'undefined' || typeof(Account[type][param]) == 'undefined' || Account[type][param] == "") {
60                         if (!params[i].optional) {
61                             self.props.flux.actions.global.showNotification("Please fill all account details");
62                             return;
63                         }
64                     }
65                 }
66             }
67
68             let nestedParams = Account.nestedParams && Account.nestedParams;
69             if (nestedParams && nestedParams.params) {
70                 for (let i = 0; i < nestedParams.params.length; i++) {
71                     let nestedParam = nestedParams.params[i].ref;
72                     if (typeof(Account[type]) == 'undefined' || typeof(Account[type][nestedParams['container-name']][nestedParam]) == 'undefined' || Account[type][nestedParams['container-name']][nestedParam] == "") {
73                         if (!nestedParams.params[i].optional) {
74                             self.props.flux.actions.global.showNotification("Please fill all account details");
75                             return;
76                         }
77                     }
78                 }
79             }
80         }
81
82         let newAccount = _cloneDeep(removeTrailingWhitespace(Account));
83         delete newAccount.params;
84         newAccount.nestedParams &&
85             newAccount.nestedParams['container-name'] &&
86             delete newAccount[newAccount.nestedParams['container-name']];
87         delete newAccount.nestedParams;
88
89         this.props.flux.actions.global.showScreenLoader();
90         this.props.store.create(newAccount, AccountType).then(function() {
91             self.props.router.push({pathname:'accounts'});
92             self.props.flux.actions.global.hideScreenLoader.defer();
93         },
94          function() {
95             self.props.flux.actions.global.showNotification("There was an error creating your account. Please contact your system administrator.");
96             self.props.flux.actions.global.hideScreenLoader.defer();
97          });
98     }
99     update(e) {
100         e.preventDefault();
101         var self = this;
102         var Account = this.state.account;
103         let AccountType = this.state.accountType;
104         this.props.flux.actions.global.showScreenLoader();
105         this.props.store.update(Account, AccountType).then(function() {
106             self.props.router.push({pathname:'accounts'});
107              self.props.flux.actions.global.hideScreenLoader();
108         },
109         function() {
110
111         });
112     }
113     cancel = (e) => {
114         e.preventDefault();
115         e.stopPropagation();
116         this.props.router.push({pathname:'accounts'});
117     }
118     handleDelete = () => {
119         let self = this;
120         let msg = 'Preparing to delete "' + self.state.account.name + '"' +
121         ' Are you sure you want to delete this ' + self.state.accountType + ' account?"';
122         if (window.confirm(msg)) {
123             this.props.store.delete(self.state.accountType, self.state.account.name).then(function() {
124                 self.props.flux.actions.global.hideScreenLoader();
125                 self.props.router.push({pathname:'accounts'});
126             }, function(){
127                 // self.props.flux.actions.global.hideScreenLoader.defer();
128                 // console.log('Delete Account Fail');
129             });
130         } else {
131            self.props.flux.actions.global.hideScreenLoader();
132         }
133     }
134     handleNameChange(event) {
135        this.props.store.handleNameChange(event);
136     }
137     handleAccountTypeChange(node, event) {
138         this.props.store.handleAccountTypeChange(node, event);
139     }
140     handleSelectSdnAccount = (e) => {
141         var tmp = this.state.account;
142         if(e) {
143             tmp['sdn-account'] = e;
144         } else {
145             if(tmp['sdn-account']) {
146                 delete tmp['sdn-account'];
147             }
148         }
149         console.log(e, tmp)
150     }
151     preventDefault = (e) => {
152         e.preventDefault();
153         e.stopPropagation();
154     }
155     evaluateSubmit = (e) => {
156         if (e.keyCode == 13) {
157             if (this.props.edit) {
158                 this.update(e);
159             } else {
160                 this.create(e);
161             }
162             e.preventDefault();
163             e.stopPropagation();
164         }
165     }
166
167     render() {
168         let self = this;
169         let {store, ...props} = this.props;
170         // This section builds elements that only show up on the create page.
171         // var name = <label>Name <input type="text" onChange={this.handleNameChange.bind(this)} style={{'textAlign':'left'}} /></label>;
172         var name = <TextInput label="Name"  onChange={this.handleNameChange.bind(this)} required={true} />;
173         let params = null;
174         let selectAccount = null;
175         let resfreshStatus = null;
176         let Account = this.state.account;
177         // AccountType is for the view, not the data account-type value;
178         let AccountType = this.state.accountType;
179         let Types = this.state.types;
180         let isEdit = this.props.params.name != 'create';
181         var buttons;
182         let cloudResources = Account['cloud-resources-state'] && Account['cloud-resources-state'][Account['account-type']];
183         let cloudResourcesStateHTML = null;
184
185         // Account Type Radio
186         var selectAccountStack = [];
187         if (!isEdit) {
188             buttons = [
189                 <Button key="0" onClick={this.cancel} className="cancel light" label="Cancel"></Button>,
190                 <Button key="1" role="button" onClick={this.create.bind(this)} className="save dark" label="Save" />
191             ]
192             for (var i = 0; i < Types.length; i++) {
193                 var node = Types[i];
194                 var isSelected = (Account['account-type'] == node['account-type']);
195                 selectAccountStack.push(
196                   <label key={i} className={"accountSelection " + (isSelected ? "accountSelection--isSelected" : "")}>
197                     <div className={"accountSelection-overlay" + (isSelected ? "accountSelection-overlay--isSelected" : "")}></div>
198                     <div className="accountSelection-imageWrapper">
199                         <img src={store.getImage(node['account-type'])}/>
200                     </div>
201                     <input type="radio" name="account" onChange={this.handleAccountTypeChange.bind(this, node)} defaultChecked={node.name == Types[0].name} value={node['account-type']} />{node.name}
202                   </label>
203                 )
204             }
205             selectAccount = (
206                 <div className="accountForm">
207                     <h3 className="accountForm-title">Select Account Type</h3>
208                     <div className="select-type accountForm-content" >
209                         {selectAccountStack}
210                     </div>
211                 </div>
212             );
213         } else {
214             selectAccount = null
215         }
216
217          //
218         // This sections builds the parameters for the account details.
219         if (Account.params) {
220             var paramsStack = [];
221             var optionalField = '';
222             for (var i = 0; i < Account.params.length; i++) {
223                 var node = Account.params[i];
224                 var value = ""
225                 if (Account[Account['account-type']]) {
226                     value = Account[Account['account-type']][node.ref]
227                 }
228                 if (this.props.edit && Account.params) {
229                     value = Account.params[node.ref];
230                 }
231                 paramsStack.push(
232                     <TextInput key={node.label} className="accountForm-input" label={node.label} required={!node.optional}  onChange={this.props.store.handleParamChange(node)} value={value} />
233                 );
234             }
235             params = (
236                 <li className="create-fleet-pool accountForm">
237                     <h3  className="accountForm-title"> {isEdit ? 'Update' : 'Enter'} Account Details</h3>
238                     <div className="accountForm-content">
239                         {paramsStack}
240                     </div>
241                 </li>
242             )
243         } else {
244             params = (
245                 <li className="create-fleet-pool accountForm">
246                     <h3 className="accountForm-title"> {isEdit ? 'Update' : 'Enter'}</h3>
247                     <label style={{'marginLeft':'17px', color:'#888'}}>No Details Required</label>
248                 </li>
249             )
250         }
251
252         // This section builds elements that only show up in the edit page.
253         if (isEdit) {
254             name = <label>{Account.name}</label>;
255             buttons = [
256                 <Button key="2" onClick={this.handleDelete} className="light" label="Remove Account" />,
257                 <Button key="3" onClick={this.cancel} className="light" label="Cancel" />,
258                 <Button key="4" role="button" onClick={this.update.bind(this)} className="update dark" label="Update" />
259             ];
260             resfreshStatus = Account['connection-status'] ? (
261                 <div className="accountForm">
262                     <div className="accountForm-title accountForm-title--edit">
263                         Connection Status
264                     </div>
265                     <div className="accountForm-content" style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center'}}>
266                         <div style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center'}}>
267                             <AccountConnectivityStatus status={Account['connection-status'].status} />
268                             {Account['connection-status'].status.toUpperCase()}
269                         </div>
270                             <Button className="refreshList light" onClick={this.props.store.refreshAccount.bind(this, Account.name, AccountType)} label="REFRESH STATUS"></Button>
271                     </div>
272                     {
273                         Account['connection-status'].status.toUpperCase() === 'FAILURE' ?
274                         displayFailureMessage(Account['connection-status'].details) : null
275                     }
276                 </div>
277             ) : null;
278             // cloudResourcesStateHTML = (
279             //     <div className="accountForm">
280             //         <h3 className="accountForm-title">Resources Status</h3>
281             //         <div className="accountForm-content" >
282             //         <ul>
283             //             {
284             //                 cloudResources && props.AccountMeta.resources[Account['account-type']].map(function(r, i) {
285
286             //                     return (
287             //                         <li key={i}>
288             //                             {r}: {cloudResources[r]}
289             //                         </li>
290             //                     )
291             //                 }) || 'No Additional Resources'
292             //             }
293             //         </ul>
294             //         </div>
295             //     </div>
296             // )
297         }
298
299         var html = (
300
301               <form className="app-body create Accounts"  onSubmit={this.preventDefault} onKeyDown={this.evaluateSubmit}>
302                 <div className="noticeSubText noticeSubText_right">
303                     * required
304                 </div>
305                 <div className="associateSdnAccount accountForm">
306                     <h3 className="accountForm-title">Account</h3>
307                     <div className="accountForm-content" style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center'}}>
308                         <h4 style={{flex: '1'}}>{name}</h4>
309                         { isEdit ?
310                             (
311                                 <div style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center'}}>
312                                     <img src={store.getImage(Account['account-type'])}/> {props.AccountMeta.labelByType[Account['account-type']]}
313                                 </div>)
314                             : null
315                         }
316                     </div>
317                 </div>
318
319                   {selectAccount}
320                   {sdnAccounts}
321                   {resfreshStatus}
322                   {cloudResourcesStateHTML}
323                   <ol className="flex-row">
324                       {params}
325                   </ol>
326                   <div className="form-actions">
327                       {buttons}
328                   </div>
329               </form>
330         )
331         return html;
332     }
333 }
334
335 function displayFailureMessage(msg) {
336     return (
337         <div className="accountForm-content" style={{maxWidth: '600px'}}>
338             <div style={{paddingBottom: '1rem'}}>Details:</div>
339             <div>
340                 {msg}
341             </div>
342
343         </div>
344     )
345 }
346
347 class SelectOption extends React.Component {
348   constructor(props){
349     super(props);
350   }
351   handleOnChange = (e) => {
352     this.props.onChange(JSON.parse(e.target.value));
353   }
354   render() {
355     let html;
356     html = (
357       <select className={this.props.className} onChange={this.handleOnChange}>
358         {
359           this.props.options.map(function(op, i) {
360             return <option key={i} value={JSON.stringify(op.value)}>{op.label}</option>
361           })
362         }
363       </select>
364     );
365     return html;
366   }
367 }
368 SelectOption.defaultProps = {
369   options: [],
370   onChange: function(e) {
371     console.dir(e)
372   }
373 }
374
375 function removeTrailingWhitespace(Account) {
376              var type = Account['account-type'];
377             var params = Account.params;
378
379             if(params) {
380                 for (var i = 0; i < params.length; i++) {
381                     var param = params[i].ref;
382                     if(typeof(Account[type][param]) == 'string') {
383                         Account[type][param] = Account[type][param].trim();
384                     }
385                 }
386             }
387
388             let nestedParams = Account.nestedParams;
389             if (nestedParams && nestedParams.params) {
390                 for (let i = 0; i < nestedParams.params.length; i++) {
391                     let nestedParam = nestedParams.params[i].ref;
392                     let nestedParamValue = Account[type][nestedParams['container-name']][nestedParam];
393                     if (typeof(nestedParamValue) == 'string') {
394                         Account[type][nestedParams['container-name']][nestedParam] = nestedParamValue.trim();
395                     }
396                 }
397             }
398             return Account;
399 }
400
401 export default SkyquakeComponent(Account)