Create wt-odlux directory
[ccsdk/features.git] / sdnr / wt-odlux / odlux / apps / mediatorApp / src / components / editMediatorConfigDialog.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 * as React from 'react';
19 import { Theme, Typography, FormControlLabel, Checkbox } from '@mui/material';
20
21 import { WithStyles } from '@mui/styles';
22 import createStyles from '@mui/styles/createStyles';
23 import withStyles from '@mui/styles/withStyles';
24
25 import Button from '@mui/material/Button';
26 import TextField from '@mui/material/TextField';
27 import Select from '@mui/material/Select';
28 import Dialog from '@mui/material/Dialog';
29 import DialogActions from '@mui/material/DialogActions';
30 import DialogContent from '@mui/material/DialogContent';
31 import DialogContentText from '@mui/material/DialogContentText';
32 import DialogTitle from '@mui/material/DialogTitle';
33
34 import Tabs from '@mui/material/Tabs';
35 import Tab from '@mui/material/Tab';
36
37 import Fab from '@mui/material/Fab';
38 import AddIcon from '@mui/icons-material/Add';
39 import DeleteIcon from '@mui/icons-material/Delete';
40 import IconButton from '@mui/material/IconButton';
41
42 import { addMediatorConfigAsyncActionCreator, updateMediatorConfigAsyncActionCreator, removeMediatorConfigAsyncActionCreator } from '../actions/mediatorConfigActions';
43 import { MediatorConfig, ODLConfig } from '../models/mediatorServer';
44 import FormControl from '@mui/material/FormControl';
45 import InputLabel from '@mui/material/InputLabel';
46 import MenuItem from '@mui/material/MenuItem';
47
48 import { Panel } from '../../../../framework/src/components/material-ui/panel';
49
50 import { IDispatcher, connect, Connect } from '../../../../framework/src/flux/connect';
51 import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
52
53
54 const styles = (theme: Theme) => createStyles({
55   root: {
56     display: 'flex',
57     flexDirection: 'column',
58     flex: '1',
59   },
60   fab: {
61     position: 'absolute',
62     bottom: theme.spacing(1),
63     right: theme.spacing(1),
64   },
65   title: {
66     fontSize: 14,
67   },
68   center: {
69     flex: "1",
70     display: "flex",
71     alignItems: "center",
72     justifyContent: "center",
73   },
74   alignInOneLine: {
75     display: 'flex',
76     flexDirection: 'row'
77   },
78   left: {
79     marginRight: theme.spacing(1),
80   },
81   right: {
82     marginLeft: 0,
83   }
84 });
85
86 const TabContainer: React.SFC = ({ children }) => {
87   return (
88     <div style={{ width: "430px", height: "530px", position: "relative", display: 'flex', flexDirection: 'column' }}>
89       {children}
90     </div>
91   );
92 }
93
94 export enum EditMediatorConfigDialogMode {
95   None = "none",
96   AddMediatorConfig = "addMediatorConfig",
97   EditMediatorConfig = "editMediatorConfig",
98   RemoveMediatorConfig = "removeMediatorConfig",
99 }
100
101 const mapProps = (state: IApplicationStoreState) => ({
102   supportedDevices: state.mediator.mediatorServerState.supportedDevices
103 });
104
105 const mapDispatch = (dispatcher: IDispatcher) => ({
106   addMediatorConfig: (config: MediatorConfig) => {
107     dispatcher.dispatch(addMediatorConfigAsyncActionCreator(config));
108   },
109   updateMediatorConfig: (config: MediatorConfig) => {
110     dispatcher.dispatch(updateMediatorConfigAsyncActionCreator(config));
111   },
112   removeMediatorConfig: (config: MediatorConfig) => {
113     dispatcher.dispatch(removeMediatorConfigAsyncActionCreator(config));
114   },
115 });
116
117 type DialogSettings = {
118   dialogTitle: string;
119   dialogDescription: string;
120   applyButtonText: string;
121   cancelButtonText: string;
122   readonly: boolean;
123   readonlyName: boolean;
124 };
125
126 const settings: { [key: string]: DialogSettings } = {
127   [EditMediatorConfigDialogMode.None]: {
128     dialogTitle: "",
129     dialogDescription: "",
130     applyButtonText: "",
131     cancelButtonText: "",
132     readonly: true,
133     readonlyName: true,
134   },
135   [EditMediatorConfigDialogMode.AddMediatorConfig]: {
136     dialogTitle: "Add Mediator Configuration",
137     dialogDescription: "",
138     applyButtonText: "Add",
139     cancelButtonText: "Cancel",
140     readonly: false,
141     readonlyName: false,
142   },
143   [EditMediatorConfigDialogMode.EditMediatorConfig]: {
144     dialogTitle: "Edit Mediator Configuration",
145     dialogDescription: "",
146     applyButtonText: "Update",
147     cancelButtonText: "Cancel",
148     readonly: false,
149     readonlyName: true,
150   },
151   [EditMediatorConfigDialogMode.RemoveMediatorConfig]: {
152     dialogTitle: "Remove Mediator Configuration",
153     dialogDescription: "",
154     applyButtonText: "Remove",
155     cancelButtonText: "Cancel",
156     readonly: true,
157     readonlyName: true,
158   },
159 };
160
161 type EditMediatorConfigDialogComponentProps = WithStyles<typeof styles> & Connect<typeof mapProps, typeof mapDispatch> & {
162   mode: EditMediatorConfigDialogMode;
163   mediatorConfig: MediatorConfig;
164   onClose: () => void;
165 };
166
167 type EditMediatorConfigDialogComponentState = MediatorConfig & { activeTab: number; activeOdlConfig: string, forceAddOdlConfig: boolean, isOdlConfigHostnameEmpty: boolean };
168
169 class EditMediatorConfigDialogComponent extends React.Component<EditMediatorConfigDialogComponentProps, EditMediatorConfigDialogComponentState> {
170   constructor(props: EditMediatorConfigDialogComponentProps) {
171     super(props);
172
173     this.state = {
174       ...this.props.mediatorConfig,
175       activeTab: 0,
176       activeOdlConfig: "",
177       forceAddOdlConfig: false,
178       isOdlConfigHostnameEmpty: false
179     };
180   }
181
182   private odlConfigValueChangeHandlerCreator = <THtmlElement extends HTMLElement, TKey extends keyof ODLConfig>(index: number, property: TKey, mapValue: (event: React.ChangeEvent<THtmlElement>) => any) => (event: React.ChangeEvent<THtmlElement>) => {
183     event.stopPropagation();
184     event.preventDefault();
185     this.setState({
186       ODLConfig: [
187         ...this.state.ODLConfig.slice(0, index),
188         { ...this.state.ODLConfig[index], [property]: mapValue(event) },
189         ...this.state.ODLConfig.slice(index + 1)
190       ]
191     });
192   }
193
194   render(): JSX.Element {
195     const setting = settings[this.props.mode];
196     const { classes } = this.props;
197     return (
198       <Dialog open={this.props.mode !== EditMediatorConfigDialogMode.None}>
199         <DialogTitle id="form-dialog-title">{setting.dialogTitle}</DialogTitle>
200         <DialogContent>
201           <DialogContentText>
202             {setting.dialogDescription}
203           </DialogContentText>
204           <Tabs value={this.state.activeTab} indicatorColor="secondary" textColor="secondary" onChange={(event, value) => this.setState({ activeTab: value })} >
205             <Tab label="Config" />
206             <Tab label="ODL AutoConnect" />
207           </Tabs>
208           {this.state.activeTab === 0 ? <TabContainer >
209             <TextField variant="standard" disabled={setting.readonly || setting.readonlyName} spellCheck={false} autoFocus margin="dense" id="name" label="Name" type="text" fullWidth value={this.state.Name} onChange={(event) => { this.setState({ Name: event.target.value }); }} />
210             <FormControl variant="standard" fullWidth disabled={setting.readonly}>
211               <InputLabel htmlFor="deviceType">Device</InputLabel>
212               <Select variant="standard" value={this.state.DeviceType} onChange={(event, value) => {
213                 const device = this.props.supportedDevices.find(device => device.id === event.target.value);
214                 if (device) {
215                   this.setState({
216                     DeviceType: device.id,
217                     NeXMLFile: device.xml
218                   });
219                 } else {
220                   this.setState({
221                     DeviceType: -1,
222                     NeXMLFile: ""
223                   });
224                 }
225               }} inputProps={{ name: 'deviceType', id: 'deviceType' }} fullWidth >
226                 <MenuItem value={-1}><em>None</em></MenuItem>
227                 {this.props.supportedDevices.map(device => (<MenuItem key={device.id} value={device.id} >{`${device.vendor} - ${device.device} (${device.version || '0.0.0'}) `}</MenuItem>))}
228               </Select>
229             </FormControl>
230             <TextField variant="standard" disabled={setting.readonly} spellCheck={false} autoFocus margin="dense" id="ipAddress" label="Device IP" type="text" fullWidth value={this.state.DeviceIp} onChange={(event) => { this.setState({ DeviceIp: event.target.value }); }} />
231             <TextField variant="standard" disabled={setting.readonly} spellCheck={false} autoFocus margin="dense" id="devicePort" label="Device SNMP Port" type="number" fullWidth value={this.state.DevicePort || ""} onChange={(event) => { this.setState({ DevicePort: +event.target.value }); }} />
232             <TextField variant="standard" disabled={setting.readonly} spellCheck={false} autoFocus margin="dense" id="trapsPort" label="TrapsPort" type="number" fullWidth value={this.state.TrapPort || ""} onChange={(event) => { this.setState({ TrapPort: +event.target.value }); }} />
233             <TextField variant="standard" disabled={setting.readonly} spellCheck={false} autoFocus margin="dense" id="ncUser" label="Netconf User" type="text" fullWidth value={this.state.NcUsername} onChange={(event) => { this.setState({ NcUsername: event.target.value }); }} />
234             <TextField variant="standard" disabled={setting.readonly} spellCheck={false} autoFocus margin="dense" id="ncPassword" label="Netconf Password" type="password" fullWidth value={this.state.NcPassword} onChange={(event) => { this.setState({ NcPassword: event.target.value }); }} />
235             <TextField variant="standard" disabled={setting.readonly} spellCheck={false} autoFocus margin="dense" id="ncPort" label="Netconf Port" type="number" fullWidth value={this.state.NcPort || ""} onChange={(event) => { this.setState({ NcPort: +event.target.value }); }} />
236           </TabContainer> : null}
237           {this.state.activeTab === 1 ? <TabContainer >
238             {this.state.ODLConfig && this.state.ODLConfig.length > 0
239               ? this.state.ODLConfig.map((cfg, ind) => {
240                 const panelId = `panel-${ind}`;
241                 const deleteButton = (<IconButton
242                   onClick={() => {
243                     this.setState({
244                       ODLConfig: [
245                         ...this.state.ODLConfig.slice(0, ind),
246                         ...this.state.ODLConfig.slice(ind + 1)
247                       ]
248                     });
249                   }}
250                   size="large"><DeleteIcon /></IconButton>)
251                 return (
252                   <Panel title={cfg.Server && `${cfg.User ? `${cfg.User}@` : ''}${cfg.Protocol}://${cfg.Server}:${cfg.Port}` || "new odl config"} key={panelId} panelId={panelId} activePanel={this.state.activeOdlConfig} customActionButtons={[deleteButton]}
253                     onToggle={(id) => { this.setState({ activeOdlConfig: (this.state.activeOdlConfig === id) ? "" : (id || "") }); console.log("activeOdlConfig " + id); this.hideHostnameErrormessage(id) }} >
254                     <div className={classes.alignInOneLine}>
255                       <FormControl variant="standard" className={classes.left} margin={"dense"} >
256                         <InputLabel htmlFor={`protocol-${ind}`}>Protocoll</InputLabel>
257                         <Select variant="standard"  value={cfg.Protocol} onChange={(e, v) => this.odlConfigValueChangeHandlerCreator(ind, "Protocol", e => v)} inputProps={{ name: `protocol-${ind}`, id: `protocol-${ind}` }} fullWidth >
258                           <MenuItem value={"http"}>http</MenuItem>
259                           <MenuItem value={"https"}>https</MenuItem>
260                         </Select>
261                       </FormControl>
262                       <TextField variant="standard" className={classes.left} spellCheck={false} margin="dense" id="hostname" label="Hostname" type="text" value={cfg.Server} onChange={this.odlConfigValueChangeHandlerCreator(ind, "Server", e => e.target.value)} />
263                       <TextField variant="standard" className={classes.right} style={{ maxWidth: "65px" }} spellCheck={false} margin="dense" id="port" label="Port" type="number" value={cfg.Port || ""} onChange={this.odlConfigValueChangeHandlerCreator(ind, "Port", e => +e.target.value)} />
264                     </div>
265                     {
266                       this.state.isOdlConfigHostnameEmpty &&
267                       <Typography component={"div"} className={classes.left} color="error" gutterBottom>Please add a hostname.</Typography>
268                     }
269                     <div className={classes.alignInOneLine}>
270                       <TextField variant="standard" className={classes.left} spellCheck={false} margin="dense" id="username" label="Username" type="text" value={cfg.User} onChange={this.odlConfigValueChangeHandlerCreator(ind, "User", e => e.target.value)} />
271                       <TextField variant="standard" className={classes.right} spellCheck={false} margin="dense" id="password" label="Password" type="password" value={cfg.Password} onChange={this.odlConfigValueChangeHandlerCreator(ind, "Password", e => e.target.value)} />
272                     </div>
273                     <div className={classes.alignInOneLine}>
274                       <FormControlLabel className={classes.right} control={<Checkbox checked={cfg.Trustall} onChange={this.odlConfigValueChangeHandlerCreator(ind, "Trustall", e => e.target.checked)} />} label="Trustall" />
275                     </div>
276                   </Panel>
277                 );
278               })
279               : (this.state.forceAddOdlConfig ?
280                 <div className={classes.center} >
281                   <Typography component={"div"} className={classes.title} color="error" gutterBottom>Please add at least one ODL auto connect configuration.</Typography>
282                 </div>
283                 :
284                 <div className={classes.center} >
285                   <Typography component={"div"} className={classes.title} color="textSecondary" gutterBottom>Please add an ODL auto connect configuration.</Typography>
286                 </div>
287               )
288             }
289             <Fab className={classes.fab} color="primary" aria-label="Add" onClick={() => this.setState({
290               ODLConfig: [...this.state.ODLConfig, { Server: '', Port: 8181, Protocol: 'https', User: 'admin', Password: 'admin', Trustall: false }]
291             })} > <AddIcon /> </Fab>
292
293           </TabContainer> : null}
294
295         </DialogContent>
296         <DialogActions>
297           <Button color="inherit" onClick={(event) => { this.addConfig(event) }} > {setting.applyButtonText} </Button>
298           <Button onClick={(event) => {
299             this.onCancel();
300             event.preventDefault();
301             event.stopPropagation();
302             this.resetPanel();
303           }} color="secondary"> {setting.cancelButtonText} </Button>
304         </DialogActions>
305       </Dialog>
306     );
307   }
308
309   private addConfig = (event: any) => {
310     event.preventDefault();
311     event.stopPropagation();
312
313     if (this.state.ODLConfig.length === 0) {
314       this.setState({ activeTab: 1, forceAddOdlConfig: true });
315     }
316     else
317       if (this.state.ODLConfig.length > 0) {
318         for (let i = 0; i <= this.state.ODLConfig.length; i++) {
319           if (this.isHostnameEmpty(i)) {
320             this.setState({ activeOdlConfig: 'panel-' + i })
321             this.setState({ isOdlConfigHostnameEmpty: true })
322             return;
323           }
324         }
325
326         this.onApply(Object.keys(this.state).reduce<MediatorConfig & { [kex: string]: any }>((acc, key) => {
327           // do not copy additional state properties
328           if (key !== "activeTab" && key !== "activeOdlConfig" && key !== "isOdlConfigHostnameEmpty"
329             && key !== "forceAddOdlConfig" && key !== "_initialMediatorConfig") acc[key] = (this.state as any)[key];
330           return acc;
331         }, {} as MediatorConfig));
332         this.resetPanel();
333       }
334   }
335
336   private resetPanel = () => {
337     this.setState({ forceAddOdlConfig: false, isOdlConfigHostnameEmpty: false, activeTab: 0 });
338   }
339
340
341   private hideHostnameErrormessage = (panelId: string | null) => {
342
343     if (panelId) {
344       let id = Number(panelId.split('-')[1]);
345       if (!this.isHostnameEmpty(id)) {
346         this.setState({ isOdlConfigHostnameEmpty: false })
347       }
348     }
349   }
350
351   private isHostnameEmpty = (id: number) => {
352
353     const element = this.state.ODLConfig[id];
354     if (element) {
355       if (!element.Server) {
356         return true;
357       }
358       else {
359         return false
360       }
361
362     } else {
363       return null;
364     }
365   }
366
367   private onApply = (config: MediatorConfig) => {
368     this.props.onClose && this.props.onClose();
369     switch (this.props.mode) {
370       case EditMediatorConfigDialogMode.AddMediatorConfig:
371         config && this.props.addMediatorConfig(config);
372         break;
373       case EditMediatorConfigDialogMode.EditMediatorConfig:
374         config && this.props.updateMediatorConfig(config);
375         break;
376       case EditMediatorConfigDialogMode.RemoveMediatorConfig:
377         config && this.props.removeMediatorConfig(config);
378         break;
379     }
380   };
381
382   private onCancel = () => {
383     this.props.onClose && this.props.onClose();
384   }
385
386   static getDerivedStateFromProps(props: EditMediatorConfigDialogComponentProps, state: EditMediatorConfigDialogComponentState & { _initialMediatorConfig: MediatorConfig }): EditMediatorConfigDialogComponentState & { _initialMediatorConfig: MediatorConfig } {
387     if (props.mediatorConfig !== state._initialMediatorConfig) {
388       state = {
389         ...state,
390         ...props.mediatorConfig,
391         _initialMediatorConfig: props.mediatorConfig,
392       };
393     }
394     return state;
395   }
396 }
397
398 export const EditMediatorConfigDialog = withStyles(styles)(connect(mapProps, mapDispatch)(EditMediatorConfigDialogComponent));
399 export default EditMediatorConfigDialog;