user mgmt, project mgmt, clean up and styling
[osm/UI.git] / skyquake / plugins / user_management / src / dashboard / dashboard.jsx
1 /*
2  * STANDARD_RIFT_IO_COPYRIGHT
3  */
4
5 import React from 'react';
6 import ReactDOM from 'react-dom';
7 import AppHeader from 'widgets/header/header.jsx';
8 import UserManagementStore from './userMgmtStore.js';
9 import SkyquakeComponent from 'widgets/skyquake_container/skyquakeComponent.jsx';
10 import 'style/layout.scss';
11 import './userMgmt.scss';
12 import {Panel, PanelWrapper} from 'widgets/panel/panel';
13
14
15 import TextInput from 'widgets/form_controls/textInput.jsx';
16 import Input from 'widgets/form_controls/input.jsx';
17 import Button, {ButtonGroup} from 'widgets/button/sq-button.jsx';
18 import SelectOption from 'widgets/form_controls/selectOption.jsx';
19 import 'widgets/form_controls/formControls.scss';
20 import imgAdd from '../../node_modules/open-iconic/svg/plus.svg'
21 import imgRemove from '../../node_modules/open-iconic/svg/trash.svg';
22
23 class UserManagementDashboard extends React.Component {
24     constructor(props) {
25         super(props);
26         this.Store = this.props.flux.stores.hasOwnProperty('UserManagementStore') ? this.props.flux.stores.UserManagementStore : this.props.flux.createStore(UserManagementStore);
27        this.state = this.Store.getState();
28        this.actions = this.state.actions;
29
30     }
31     componentDidUpdate() {
32         let self = this;
33         ReactDOM.findDOMNode(this.UserList).addEventListener('transitionend', this.onTransitionEnd, false);
34         setTimeout(function() {
35             let element = self[`user-ref-${self.state.activeIndex}`]
36             element && !isElementInView(element) && element.scrollIntoView({block: 'end', behavior: 'smooth'});
37         })
38     }
39     componentWillMount() {
40         this.Store.listen(this.updateState);
41         this.Store.getUsers();
42     }
43     componentWillUnmount() {
44         this.Store.unlisten(this.updateState);
45     }
46     updateState = (state) => {
47         this.setState(state);
48     }
49     updateInput = (key, e) => {
50         let property = key;
51         this.actions.handleUpdateInput({
52             [property]:e.target.value
53         })
54     }
55     disabledChange = (e) => {
56         this.actions.handleDisabledChange(e.target.checked);
57     }
58     platformChange = (platformRole, e) => {
59         this.actions.handlePlatformRoleUpdate(platformRole, e.currentTarget.checked);
60     }
61     addProjectRole = (e) => {
62         this.actions.handleAddProjectItem();
63     }
64     removeProjectRole = (i, e) => {
65         this.actions.handleRemoveProjectItem(i);
66     }
67     updateProjectRole = (i, e) => {
68         this.actions.handleUpdateProjectRole(i, e)
69     }
70     addUser = () => {
71         this.actions.handleAddUser();
72     }
73     viewUser = (un, index) => {
74         this.actions.viewUser(un, index);
75     }
76     editUser = () => {
77         this.actions.editUser(false);
78     }
79     cancelEditUser = () => {
80         this.actions.editUser(true)
81     }
82     closePanel = () => {
83         this.actions.handleCloseUserPanel();
84     }
85     // updateUser = (e) => {
86     //     e.preventDefault();
87     //     e.stopPropagation();
88
89     //     this.Store.updateUser();
90     // }
91     deleteUser = (e) => {
92         e.preventDefault();
93         e.stopPropagation();
94         this.Store.deleteUser({
95                 'user-name': this.state['user-name'],
96                 'user-domain': this.state['user-domain']
97             });
98     }
99     createUser = (e) => {
100         e.preventDefault();
101         e.stopPropagation();
102         if(this.state['new-password'] != this.state['confirm-password']) {
103             this.props.actions.showNotification('Passwords do not match')
104         } else {
105             this.Store.createUser({
106                 'user-name': this.state['user-name'],
107                 'user-domain': this.state['user-domain'],
108                 'password': this.state['new-password']
109                 // 'confirm-password': this.state['confirm-password']
110             });
111         }
112     }
113     updateUser = (e) => {
114         let self = this;
115         e.preventDefault();
116         e.stopPropagation();
117         let validatedPasswords = validatePasswordFields(this.state);
118         if(validatedPasswords) {
119             this.Store.updateUser(_.merge({
120                             'user-name': this.state['user-name'],
121                             'user-domain': this.state['user-domain'],
122                             'password': this.state['new-password']
123                         }));
124         }
125         function validatePasswordFields(state) {
126             let oldOne = state['old-password'];
127             let newOne = state['new-password'];
128             let confirmOne = state['confirm-password'];
129             if(true) {
130                 if(oldOne == newOne) {
131                     self.props.actions.showNotification('Your new password must not match your old one');
132                     return false;
133                 }
134                 if(newOne != confirmOne) {
135                     self.props.actions.showNotification('Passwords do not match');
136                     return false;
137                 }
138                 return {
139                     // 'old-password': oldOne,
140                     'new-password': newOne,
141                     'confirm-password': confirmOne
142                 }
143             } else {
144                 return {};
145             }
146         }
147     }
148      evaluateSubmit = (e) => {
149         if (e.keyCode == 13) {
150             if (this.props.isEdit) {
151                 this.updateUser(e);
152             } else {
153                 this.createUser(e);
154             }
155             e.preventDefault();
156             e.stopPropagation();
157         }
158     }
159     onTransitionEnd = (e) => {
160         this.actions.handleHideColumns(e);
161         console.log('transition end')
162     }
163     disableChange = (e) => {
164         let value = e.target.value;
165         value = value.toUpperCase();
166         if (value=="TRUE") {
167             value = true;
168         } else {
169             value = false;
170         }
171         console.log(value)
172     }
173     render() {
174         let self = this;
175         let html;
176         let props = this.props;
177         let state = this.state;
178         let passwordSectionHTML = null;
179         let formButtonsHTML = (
180             <ButtonGroup className="buttonGroup">
181                 <Button label="EDIT" type="submit" onClick={this.editUser} />
182             </ButtonGroup>
183         );
184         if(!this.state.isReadOnly) {
185             passwordSectionHTML = ( this.state.isEdit ?
186                                         (
187                                             <FormSection title="PASSWORD CHANGE">
188                                                 <Input label="NEW PASSWORD" type="password" value={state['new-password']}  onChange={this.updateInput.bind(null, 'new-password')}/>
189                                                 <Input label="REPEAT NEW PASSWORD" type="password"  value={state['confirm-password']}  onChange={this.updateInput.bind(null, 'confirm-password')}/>
190                                             </FormSection>
191                                         ) :
192                                         (
193                                             <FormSection title="CREATE PASSWORD">
194                                                 <Input label="CREATE PASSWORD" type="password" value={state.newPassword}  onChange={this.updateInput.bind(null, 'new-password')}/>
195                                                 <Input label="REPEAT PASSWORD" type="password"  value={state.repeatNewPassword}  onChange={this.updateInput.bind(null, 'confirm-password')}/>
196                                             </FormSection>
197                                         )
198                                     );
199             formButtonsHTML = (
200                                 state.isEdit ?
201                                 (
202                                     <ButtonGroup className="buttonGroup">
203                                         <Button label="Update" type="submit" onClick={this.updateUser} />
204                                         <Button label="Delete" onClick={this.deleteUser} />
205                                         <Button label="Cancel" onClick={this.cancelEditUser} />
206                                     </ButtonGroup>
207                                 )
208                                 : (
209                                     <ButtonGroup className="buttonGroup">
210                                         <Button label="Create" type="submit" onClick={this.createUser}  />
211                                     </ButtonGroup>
212                                 )
213                             )
214         }
215
216         html = (
217             <PanelWrapper column>
218                 <AppHeader nav={[{name: 'PLATFORM ROLE MANAGEMENT', onClick: this.context.router.push.bind(this, {pathname: '/platform'})}]}/>
219                 <PanelWrapper className={`row userManagement ${!this.state.userOpen ? 'userList-open' : ''}`} style={{'flexDirection': 'row'}} >
220                     <PanelWrapper ref={(div) => { this.UserList = div}} className={`column userList expanded ${this.state.userOpen ? 'collapsed ' : ' '} ${this.state.hideColumns ? 'hideColumns ' : ' '}`}>
221                         <Panel title="User List" style={{marginBottom: 0}} no-corners>
222                             <div className="tableRow tableRow--header">
223                                 <div className="userName">
224                                     Username
225                                 </div>
226                                 <div>
227                                     Domain
228                                 </div>
229                             </div>
230                             {state.users && state.users.map((u, k) => {
231                                 let platformRoles = [];
232                                 for(let role in u.platformRoles) {
233                                     platformRoles.push(<div>{`${role}: ${u.platformRoles[role]}`}</div>)
234                                 }
235                                 return (
236                                     <div ref={(el) => this[`user-ref-${k}`] = el} className={`tableRow tableRow--data ${((self.state.activeIndex == k) && self.state.userOpen) ? 'tableRow--data-active' : ''}`} key={k}>
237                                         <div
238                                             className={`userName userName-header ${((self.state.activeIndex == k) && self.state.userOpen) ? 'activeUser' : ''}`}
239                                             onClick={self.viewUser.bind(null, u, k)}>
240                                             {u['user-name']}
241                                         </div>
242                                         <div>
243                                             {u['user-domain']}
244                                         </div>
245
246
247                                     </div>
248                                 )
249                             })}
250                         </Panel>
251                         <ButtonGroup  className="buttonGroup" style={{margin: '0 0.5rem 0.5rem', background: '#ddd', paddingBottom: '0.5rem'}}>
252                             <Button label="Add User" onClick={this.addUser} />
253                         </ButtonGroup>
254                     </PanelWrapper>
255                     <PanelWrapper onKeyUp={this.evaluateSubmit}
256                         className={`userAdmin column`}>
257                         <Panel
258                             title={state.isEdit ? state['user-name'] : 'Create User'}
259                             style={{marginBottom: 0}}
260                             hasCloseButton={this.closePanel}
261                             no-corners>
262                             <FormSection title="USER INFO">
263                             {
264                                 this.state.isEdit ?
265                                     null
266                                     : <Input  readonly={state.isReadOnly}  label="Username" value={state['user-name']} onChange={this.updateInput.bind(null, 'user-name')} />
267                             }
268                                 <Input readonly={true} label="Domain" value={state['user-domain']}  onChange={this.updateInput.bind(null, 'user-domain')}></Input>
269
270                                 <Input type="radiogroup" readonly={state.isReadOnly} label="Disabled" value={state.disabled} options={[{value: true, label: 'YES'}, {value: false, label: 'NO'}]}  onChange={this.disableChange} />
271                             </FormSection>
272                             <FormSection title="PLATFORM ROLES" style={{display:'none'}}>
273                                 <Input label="Super Admin" onChange={this.platformChange.bind(null, 'super_admin')} checked={state.platformRoles.super_admin} type="checkbox" />
274                                 <Input label="Platform Admin" onChange={this.platformChange.bind(null, 'platform_admin')}  checked={state.platformRoles.platform_admin} type="checkbox" />
275                                 <Input label="Platform Oper" onChange={this.platformChange.bind(null, 'platform_oper')}  checked={state.platformRoles.platform_oper} type="checkbox" />
276                             </FormSection>
277                             <FormSection title="PROJECT ROLES" style={{display:'none'}}>
278                                 <InputCollection
279                                     inital={true}
280                                     type='select'
281                                     readonly={state.isReadOnly}
282                                     options={state.projectRolesOptions}
283                                     collection={state.projectRoles}
284                                     onChange={this.updateProjectRole}
285                                     AddItemFn={this.addProjectRole}
286                                     RemoveItemFn={this.removeProjectRole}
287                                     />
288                             </FormSection>
289                             {passwordSectionHTML}
290
291                         </Panel>
292                             {formButtonsHTML}
293                     </PanelWrapper>
294                 </PanelWrapper>
295             </PanelWrapper>
296         );
297         return html;
298     }
299 }
300 // onClick={this.Store.update.bind(null, Account)}
301 UserManagementDashboard.contextTypes = {
302     router: React.PropTypes.object
303 };
304
305 UserManagementDashboard.defaultProps = {
306     userList: [],
307     selectedUser: {}
308 }
309
310 export default SkyquakeComponent(UserManagementDashboard);
311
312
313 function isElementInView(el) {
314     var rect = el && el.getBoundingClientRect() || {};
315
316     return (
317         rect.top >= 0 &&
318         rect.left >= 0 &&
319         rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
320         rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
321     );
322 }
323
324
325 // isReadOnly={state.isReadOnly} disabled={state.disabled} onChange={this.disableChange}
326
327 class isDisabled extends React.Component {
328     constructor(props) {
329         super(props);
330     }
331     render() {
332         let props = this.props;
333         return (<div/>)
334     }
335 }
336
337 /**
338  * AddItemFn:
339  */
340 class InputCollection extends React.Component {
341     constructor(props) {
342         super(props);
343         this.collection = props.collection;
344     }
345     buildTextInput(onChange, v, i) {
346         return (
347             <Input
348                 readonly={this.props.readonly}
349                 style={{flex: '1 1'}}
350                 key={i}
351                 value={v}
352                 onChange= {onChange.bind(null, i)}
353             />
354         )
355     }
356     buildSelectOption(initial, options, onChange, v, i) {
357         return (
358             <SelectOption
359                 readonly={this.props.readonly}
360                 key={`${i}-${v.replace(' ', '_')}`}
361                 intial={initial}
362                 defaultValue={v}
363                 options={options}
364                 onChange={onChange.bind(null, i)}
365             />
366         );
367     }
368     showInput() {
369
370     }
371     render() {
372         const props = this.props;
373         let inputType;
374         let className = "InputCollection";
375         if (props.className) {
376             className = `${className} ${props.className}`;
377         }
378         if (props.type == 'select') {
379             inputType = this.buildSelectOption.bind(this, props.initial, props.options, props.onChange);
380         } else {
381             inputType = this.buildTextInput.bind(this, props.onChange)
382         }
383         let html = (
384             <div className="InputCollection-wrapper">
385                 {props.collection.map((v,i) => {
386                     return (
387                         <div key={i} className={className} >
388                             {inputType(v, i)}
389                             {
390                                 props.readonly ? null : <span onClick={props.RemoveItemFn.bind(null, i)} className="removeInput"><img src={imgRemove} />Remove</span>}
391                         </div>
392                     )
393                 })}
394                 { props.readonly ? null : <span onClick={props.AddItemFn} className="addInput"><img src={imgAdd} />Add</span>}
395             </div>
396         );
397         return html;
398     }
399 }
400
401 InputCollection.defaultProps = {
402     input: Input,
403     collection: [],
404     onChange: function(i, e) {
405         console.log(`
406                         Updating with: ${e.target.value}
407                         At index of: ${i}
408                     `)
409     },
410     AddItemFn: function(e) {
411         console.log(`Adding a new item to collection`)
412     },
413     RemoveItemFn: function(i, e) {
414         console.log(`Removing item from collection at index of: ${i}`)
415     }
416 }
417
418 class FormSection extends React.Component {
419     render() {
420         let className = 'FormSection ' + this.props.className;
421         let html = (
422             <div
423                 style={this.props.style}
424                 className={className}
425             >
426                 <div className="FormSection-title">
427                     {this.props.title}
428                 </div>
429                 <div className="FormSection-body">
430                     {this.props.children}
431                 </div>
432             </div>
433         );
434         return html;
435     }
436 }
437
438 FormSection.defaultProps = {
439     className: ''
440 }