Create wt-odlux directory
[ccsdk/features.git] / sdnr / wt-odlux / odlux / apps / connectApp / src / components / editNetworkElementDialog.tsx
1 /**
2  * ============LICENSE_START========================================================================
3  * ONAP : ccsdk feature sdnr wt odlux
4  * =================================================================================================
5  * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
6  * =================================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
8  * in compliance with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software distributed under the License
13  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
14  * or implied. See the License for the specific language governing permissions and limitations under
15  * the License.
16  * ============LICENSE_END==========================================================================
17  */
18 import React from 'react';
19
20 import Button from '@mui/material/Button';
21 import Dialog from '@mui/material/Dialog';
22 import DialogActions from '@mui/material/DialogActions';
23 import DialogContent from '@mui/material/DialogContent';
24 import DialogContentText from '@mui/material/DialogContentText';
25 import DialogTitle from '@mui/material/DialogTitle';
26 import FormControl from '@mui/material/FormControl';
27 import FormControlLabel from '@mui/material/FormControlLabel';
28 import InputLabel from '@mui/material/InputLabel';
29 import MenuItem from '@mui/material/MenuItem';
30 import Radio from '@mui/material/Radio';
31 import RadioGroup from '@mui/material/RadioGroup';
32 import Select from '@mui/material/Select';
33 import TextField from '@mui/material/TextField';
34 import Typography from '@mui/material/Typography';
35
36 import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect';
37
38 import { removeWebUriAction } from '../actions/commonNetworkElementsActions';
39 import { mountNetworkElementAsyncActionCreator, unmountNetworkElementAsyncActionCreator } from '../actions/mountedNetworkElementsActions';
40 import {
41   addNewNetworkElementAsyncActionCreator, editNetworkElementAsyncActionCreator, removeNetworkElementAsyncActionCreator,
42 } from '../actions/networkElementsActions';
43 import { loadAllTlsKeyListAsync } from '../actions/tlsKeyActions';
44 import { NetworkElementConnection, propertyOf, UpdateNetworkElement } from '../models/networkElementConnection';
45
46 export enum EditNetworkElementDialogMode {
47   None = 'none',
48   EditNetworkElement = 'editNetworkElement',
49   RemoveNetworkElement = 'removeNetworkElement',
50   AddNewNetworkElement = 'addNewNetworkElement',
51   MountNetworkElement = 'mountNetworkElement',
52   UnmountNetworkElement = 'unmountNetworkElement',
53 }
54
55 const mapDispatch = (dispatcher: IDispatcher) => ({
56   addNewNetworkElement: async (element: NetworkElementConnection) => {
57     await dispatcher.dispatch(addNewNetworkElementAsyncActionCreator(element));
58     await dispatcher.dispatch(mountNetworkElementAsyncActionCreator(element));
59   },
60   mountNetworkElement: (element: NetworkElementConnection) => dispatcher.dispatch(mountNetworkElementAsyncActionCreator(element)),
61   unmountNetworkElement: (element: NetworkElementConnection) => {
62     dispatcher.dispatch(unmountNetworkElementAsyncActionCreator(element && element.nodeId));
63   },
64   editNetworkElement: async (element: UpdateNetworkElement, mountElement: NetworkElementConnection) => {
65
66     const values = Object.keys(element);
67     console.log('edit element');
68     console.log(values);
69
70     //make sure properties are there in case they get renamed
71     const idProperty = propertyOf<UpdateNetworkElement>('id');
72     const isRequiredProperty = propertyOf<UpdateNetworkElement>('isRequired');
73
74
75     if (values.length === 2 && values.includes(idProperty as string) && values.includes(isRequiredProperty as string)) {
76       // do not mount network element/node, if only isRequired is changed
77       await dispatcher.dispatch(editNetworkElementAsyncActionCreator(element));
78
79     } else if (!(values.length === 1 && values.includes(idProperty as string))) { //do not edit or mount network element/node , if only id was saved into object (no changes made!)
80       await dispatcher.dispatch(editNetworkElementAsyncActionCreator(element));
81       await dispatcher.dispatch(mountNetworkElementAsyncActionCreator(mountElement));
82     }
83   },
84   removeNetworkElement: async (element: UpdateNetworkElement) => {
85     await dispatcher.dispatch(removeNetworkElementAsyncActionCreator(element));
86     dispatcher.dispatch(removeWebUriAction(element.id));
87   },
88   getAvailableTlsKeys: async () => dispatcher.dispatch(loadAllTlsKeyListAsync()),
89 });
90
91 type DialogSettings = {
92   dialogTitle: string;
93   dialogDescription: string;
94   applyButtonText: string;
95   cancelButtonText: string;
96   enableMountIdEditor: boolean;
97   enableUsernameEditor: boolean;
98   enableExtendedEditor: boolean;
99 };
100
101 const settings: { [key: string]: DialogSettings } = {
102   [EditNetworkElementDialogMode.None]: {
103     dialogTitle: '',
104     dialogDescription: '',
105     applyButtonText: '',
106     cancelButtonText: '',
107     enableMountIdEditor: false,
108     enableUsernameEditor: false,
109     enableExtendedEditor: false,
110   },
111
112   [EditNetworkElementDialogMode.AddNewNetworkElement]: {
113     dialogTitle: 'Add New Node',
114     dialogDescription: 'Add this new node:',
115     applyButtonText: 'Add node',
116     cancelButtonText: 'Cancel',
117     enableMountIdEditor: true,
118     enableUsernameEditor: true,
119     enableExtendedEditor: true,
120   },
121   [EditNetworkElementDialogMode.MountNetworkElement]: {
122     dialogTitle: 'Mount Node',
123     dialogDescription: 'Mount this node:',
124     applyButtonText: 'Mount node',
125     cancelButtonText: 'Cancel',
126     enableMountIdEditor: false,
127     enableUsernameEditor: false,
128     enableExtendedEditor: false,
129   },
130   [EditNetworkElementDialogMode.UnmountNetworkElement]: {
131     dialogTitle: 'Unmount Node',
132     dialogDescription: 'Unmount this node:',
133     applyButtonText: 'Unmount node',
134     cancelButtonText: 'Cancel',
135     enableMountIdEditor: false,
136     enableUsernameEditor: false,
137     enableExtendedEditor: false,
138   },
139   [EditNetworkElementDialogMode.EditNetworkElement]: {
140     dialogTitle: 'Modify Node',
141     dialogDescription: 'Modify this node',
142     applyButtonText: 'Modify',
143     cancelButtonText: 'Cancel',
144     enableMountIdEditor: false,
145     enableUsernameEditor: true,
146     enableExtendedEditor: false,
147   },
148   [EditNetworkElementDialogMode.RemoveNetworkElement]: {
149     dialogTitle: 'Remove Node',
150     dialogDescription: 'Do you really want to remove this node?',
151     applyButtonText: 'Remove node',
152     cancelButtonText: 'Cancel',
153     enableMountIdEditor: false,
154     enableUsernameEditor: false,
155     enableExtendedEditor: false,
156   },
157 };
158
159 type EditNetworkElementDialogComponentProps = Connect<undefined, typeof mapDispatch> & {
160   mode: EditNetworkElementDialogMode;
161   initialNetworkElement: NetworkElementConnection;
162   onClose: () => void;
163   radioChecked: string;
164 };
165
166 type EditNetworkElementDialogComponentState = NetworkElementConnection & {
167   isNameValid: boolean;
168   isHostSet: boolean;
169   isPasswordSelected: boolean;
170   isTlsSelected: boolean;
171   radioSelected: string;
172   showPasswordTextField: boolean;
173   showTlsDropdown: boolean;
174 };
175
176 class EditNetworkElementDialogComponent extends React.Component<EditNetworkElementDialogComponentProps, EditNetworkElementDialogComponentState> {
177   constructor(props: EditNetworkElementDialogComponentProps) {
178     super(props);
179     this.handleRadioChange = this.handleRadioChange.bind(this);
180     // Initialization of state is partly overwritten by update via react getDerivedStateFromProps() below.
181     // Change initialization values in parent "networkElements.tsx" in "const emptyRequireNetworkElement"
182     this.state = {
183       nodeId: this.props.initialNetworkElement.nodeId,
184       isRequired: this.props.initialNetworkElement.isRequired,
185       host: this.props.initialNetworkElement.host,
186       port: this.props.initialNetworkElement.port,
187       isNameValid: true,
188       isHostSet: true,
189       isPasswordSelected: true,
190       isTlsSelected: false,
191       radioSelected: '',
192       showPasswordTextField: true,
193       showTlsDropdown: false,
194     };
195   }
196
197   public handleRadioChange = (event: any) => {
198     this.setState({
199       radioSelected: event.target.value,
200       showPasswordTextField: event.target.value === 'password',
201       showTlsDropdown: event.target.value === 'tlsKey',
202     });
203   };
204
205   render(): JSX.Element {
206     const setting = settings[this.props.mode];
207     let { showPasswordTextField, showTlsDropdown, radioSelected } = this.state;
208     radioSelected = this.state.radioSelected.length > 0 ? this.state.radioSelected : this.props.radioChecked;
209
210     if (radioSelected === 'password') {
211       radioSelected = 'password';
212       showPasswordTextField = true;
213       showTlsDropdown = false;
214     } else if (radioSelected === 'tlsKey') {
215       radioSelected = 'tlsKey';
216       showPasswordTextField = false;
217       showTlsDropdown = true;
218     }
219
220     let tlsKeysList = this.props.state.connect.availableTlsKeys ? this.props.state.connect.availableTlsKeys.tlsKeysList ? this.props.state.connect.availableTlsKeys.tlsKeysList : [] : [];
221
222     return (
223       <Dialog open={this.props.mode !== EditNetworkElementDialogMode.None}>
224         <DialogTitle id="form-dialog-title" aria-label={`${setting.dialogTitle.replace(/ /g, '-').toLowerCase()}-dialog`}>{setting.dialogTitle}</DialogTitle>
225         <DialogContent>
226           <DialogContentText>
227             {setting.dialogDescription}
228           </DialogContentText>
229           <TextField variant="standard" disabled={!setting.enableMountIdEditor} spellCheck={false} autoFocus margin="dense"
230             id="name" label="Node ID" aria-label="name" type="text" fullWidth value={this.state.nodeId} onChange={(event) => { this.setState({ nodeId: event.target.value }); }} />
231           {!this.state.isNameValid && <Typography variant="body1" color="error">Node ID cannot be empty.</Typography>}
232           <TextField variant="standard" disabled={!setting.enableMountIdEditor} spellCheck={false} margin="dense"
233             id="ipaddress" label="Host/IP address" aria-label="ip adress" type="text" fullWidth value={this.state.host} onChange={(event) => { this.setState({ host: event.target.value }); }} />
234           {!this.state.isHostSet && <Typography variant="body1" color="error">Host/IP address cannot be empty.</Typography>}
235
236           <TextField variant="standard" disabled={!setting.enableMountIdEditor} spellCheck={false} margin="dense"
237             id="netconfport" label="NETCONF port" aria-label="netconf port" type="number" fullWidth value={this.state.port.toString()}
238             onChange={(event) => { this.setState({ port: +event.target.value }); }} />
239           {setting.enableUsernameEditor && <TextField variant="standard" disabled={!setting.enableUsernameEditor} spellCheck={false} margin="dense"
240             id="username" label="Username" aria-label="username" type="text" fullWidth value={this.state.username} onChange={(event) => { this.setState({ username: event.target.value }); }} /> || null}
241
242           {setting.enableUsernameEditor &&
243             <RadioGroup row aria-label="password-tls-key" name="password-tls-key" value={radioSelected}
244               onChange={this.handleRadioChange} >
245               <FormControlLabel aria-label="passwordSelection" value='password' control={<Radio color="secondary" />} label="Password" onChange={this.onRadioSelect} />
246               <FormControlLabel aria-label="tlsKeySelection" value='tlsKey' control={<Radio color="secondary" />} label="TlsKey" onChange={this.onRadioSelect} />
247             </RadioGroup> || null}
248
249           {setting.enableUsernameEditor && showPasswordTextField &&
250             <TextField variant="standard" disabled={!setting.enableUsernameEditor || !showPasswordTextField} spellCheck={false} margin="dense"
251               id="password" aria-label="password" type="password" fullWidth value={this.state.password}
252               onChange={(event) => { this.setState({ password: event.target.value }); }}
253             /> || null}
254
255           <FormControl variant="standard" fullWidth disabled={!setting.enableUsernameEditor}>
256             {setting.enableUsernameEditor && showTlsDropdown &&
257               <div>
258                 <InputLabel htmlFor="pass">--Select tls-key--</InputLabel>
259                 <Select variant="standard" disabled={!setting.enableUsernameEditor || !showTlsDropdown}
260                   id="tlsKey" aria-label="tlsKey" value={this.state.tlsKey} fullWidth // displayEmpty
261                   onChange={(event) => { this.setState({ tlsKey: event.target.value as any }); }}
262                   inputProps={{ name: 'tlsKey', id: 'tlsKey' }}  >
263                   <MenuItem value={''} disabled >--Select tls-key--</MenuItem>
264                   {tlsKeysList.map(tlsKey =>
265                     (<MenuItem value={tlsKey.key} key={tlsKey.key} aria-label={tlsKey.key} >{tlsKey.key}</MenuItem>))}
266                 </Select>
267               </div>
268             }
269           </FormControl>
270
271           <FormControl variant="standard" fullWidth disabled={!setting.enableUsernameEditor}>
272             <InputLabel htmlFor="active">Required</InputLabel>
273             <Select variant="standard" aria-label="required-selection" value={this.state.isRequired || false} onChange={(event) => {
274               this.setState({ isRequired: event.target.value as any as boolean });
275             }} inputProps={{ name: 'required', id: 'required' }} fullWidth >
276               <MenuItem value={true as any as string} aria-label="true">True</MenuItem>
277               <MenuItem value={false as any as string} aria-label="false">False</MenuItem>
278             </Select>
279           </FormControl>
280         </DialogContent>
281         <DialogActions>
282           <Button aria-label="dialog-confirm-button" onClick={(event) => {
283
284             if (this.areRequieredFieldsValid()) {
285               this.onApply({
286                 isRequired: this.state.isRequired,
287                 id: this.state.nodeId,
288                 nodeId: this.state.nodeId,
289                 host: this.state.host,
290                 port: this.state.port,
291                 username: this.state.username,
292                 password: this.state.password,
293                 tlsKey: this.state.tlsKey,
294               });
295             }
296             event.preventDefault();
297             event.stopPropagation();
298           }} color="inherit" > {setting.applyButtonText} </Button>
299           <Button aria-label="dialog-cancel-button" onClick={(event) => {
300             this.onCancel();
301             event.preventDefault();
302             event.stopPropagation();
303           }} color="secondary"> {setting.cancelButtonText} </Button>
304         </DialogActions>
305       </Dialog>
306     );
307   }
308
309   public renderTlsKeys = () => {
310     try {
311       this.props.getAvailableTlsKeys();
312     } catch (err) {
313       console.log(err);
314     }
315   };
316
317   public componentDidMount() {
318     this.renderTlsKeys();
319   }
320
321   public onRadioSelect = (e: any) => {
322     if (e.target.value == 'password') {
323       this.setState({ isPasswordSelected: true, isTlsSelected: false });
324     } else if (e.target.value == 'tlsKey') {
325       this.setState({ isPasswordSelected: false, isTlsSelected: true });
326     }
327   };
328
329   private onApply = (element: NetworkElementConnection) => {
330     if (this.props.onClose) this.props.onClose();
331     let updateElement: UpdateNetworkElement = {
332       id: this.state.nodeId,
333     };
334     if (this.state.isPasswordSelected) {
335       element.tlsKey = '';
336     } else if (this.state.isTlsSelected) { //check here
337       element.password = '';
338     }
339
340     switch (this.props.mode) {
341       case EditNetworkElementDialogMode.AddNewNetworkElement:
342         if (element) this.props.addNewNetworkElement(element);
343         this.setState({
344           radioSelected: '',
345           isPasswordSelected: true,
346         });
347         break;
348       case EditNetworkElementDialogMode.MountNetworkElement:
349         if (element) this.props.mountNetworkElement(element);
350         break;
351       case EditNetworkElementDialogMode.UnmountNetworkElement:
352         if (element) this.props.unmountNetworkElement(element);
353         break;
354       case EditNetworkElementDialogMode.EditNetworkElement:
355         if (this.props.initialNetworkElement.isRequired !== this.state.isRequired)
356           updateElement.isRequired = this.state.isRequired;
357         if (this.props.initialNetworkElement.username !== this.state.username)
358           updateElement.username = this.state.username;
359         if (this.props.initialNetworkElement.password !== this.state.password && this.state.isPasswordSelected) {
360           updateElement.password = this.state.password;
361           updateElement.tlsKey = '';
362         }
363         if (this.props.initialNetworkElement.tlsKey !== this.state.tlsKey && this.state.isTlsSelected) {
364           updateElement.tlsKey = this.state.tlsKey;
365           updateElement.password = '';
366         }
367         if (element) this.props.editNetworkElement(updateElement, element);
368         this.setState({
369           radioSelected: '',
370         });
371         break;
372       case EditNetworkElementDialogMode.RemoveNetworkElement:
373         if (element) this.props.removeNetworkElement(updateElement);
374         break;
375     }
376
377     this.setState({ password: '', username: '', tlsKey: '' });
378     this.resetRequieredFields();
379   };
380
381   private onCancel = () => {
382     if (this.props.onClose) this.props.onClose();
383     this.setState({ password: '', username: '', tlsKey: '', radioSelected: '' });
384     this.resetRequieredFields();
385   };
386
387   private resetRequieredFields() {
388     this.setState({ isNameValid: true, isHostSet: true });
389   }
390
391   private areRequieredFieldsValid() {
392     let areFieldsValid = true;
393
394     if (this.state.nodeId == undefined || this.state.nodeId.trim().length === 0) {
395       this.setState({ isNameValid: false });
396       areFieldsValid = false;
397     } else {
398       this.setState({ isNameValid: true });
399     }
400
401     if (this.state.host == undefined || this.state.host.trim().length === 0) {
402       this.setState({ isHostSet: false });
403       areFieldsValid = false;
404     } else {
405       this.setState({ isHostSet: true });
406     }
407
408     return areFieldsValid;
409   }
410
411   static getDerivedStateFromProps(props: EditNetworkElementDialogComponentProps, state: EditNetworkElementDialogComponentState & { initialNetworkElement: NetworkElementConnection }): EditNetworkElementDialogComponentState & { initialNetworkElement: NetworkElementConnection } {
412     let returnState = state;
413     if (props.initialNetworkElement !== state.initialNetworkElement) {
414       returnState = {
415         ...state,
416         ...props.initialNetworkElement,
417         initialNetworkElement: props.initialNetworkElement,
418       };
419     }
420     return returnState;
421   }
422 }
423
424 export const EditNetworkElementDialog = connect(undefined, mapDispatch)(EditNetworkElementDialogComponent);
425 export default EditNetworkElementDialog;