Update ODLUX
[ccsdk/features.git] / sdnr / wt / odlux / apps / connectApp / src / components / networkElements.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 } from '@mui/material/styles';
20
21 import { WithStyles } from '@mui/styles';
22 import createStyles from '@mui/styles/createStyles';
23 import withStyles from '@mui/styles/withStyles';
24
25 import AddIcon from '@mui/icons-material/Add';
26 import Refresh from '@mui/icons-material/Refresh';
27 import LinkIcon from '@mui/icons-material/Link';
28 import LinkOffIcon from '@mui/icons-material/LinkOff';
29 import RemoveIcon from '@mui/icons-material/RemoveCircleOutline';
30 import EditIcon from '@mui/icons-material/Edit';
31 import Info from '@mui/icons-material/Info';
32 import ComputerIcon from '@mui/icons-material/Computer';
33 import { MenuItem, Divider, Typography } from '@mui/material';
34
35 import { MaterialTable, ColumnType, MaterialTableCtorType } from '../../../../framework/src/components/material-table';
36 import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
37 import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect';
38 import { NavigateToApplication } from '../../../../framework/src/actions/navigationActions';
39
40 import { createNetworkElementsActions, createNetworkElementsProperties } from '../handlers/networkElementsHandler';
41
42 import { NetworkElementConnection } from '../models/networkElementConnection';
43 import { ModuleSet, TopologyNode } from '../models/topologyNetconf';
44 import EditNetworkElementDialog, { EditNetworkElementDialogMode } from './editNetworkElementDialog';
45 import RefreshNetworkElementsDialog, { RefreshNetworkElementsDialogMode } from './refreshNetworkElementsDialog';
46
47 import InfoNetworkElementDialog, { InfoNetworkElementDialogMode } from './infoNetworkElementDialog';
48 import { loadAllInfoElementAsync, loadAllInfoElementFeaturesAsync } from '../actions/infoNetworkElementActions';
49 import { connectService } from '../services/connectService';
50 import { getAccessPolicyByUrl } from '../../../../framework/src/services/restService';
51
52 const styles = (theme: Theme) => createStyles({
53   connectionStatusConnected: {
54     color: 'darkgreen',
55   },
56   connectionStatusConnecting: {
57     color: 'blue',
58   },
59   connectionStatusDisconnected: {
60     color: 'red',
61   },
62   button: {
63     margin: 0,
64     padding: "6px 6px",
65     minWidth: 'unset'
66   },
67   spacer: {
68     marginLeft: theme.spacing(1),
69     marginRight: theme.spacing(1),
70     display: "inline"
71   }
72 });
73
74 type GetStatelessComponentProps<T> = T extends (props: infer P & { children?: React.ReactNode }) => any ? P : any
75 const MenuItemExt: React.FC<GetStatelessComponentProps<typeof MenuItem>> = (props) => {
76   const [disabled, setDisabled] = React.useState(true);
77   const onMouseDown = (ev: React.MouseEvent<HTMLElement>) => {
78     if (ev.button === 1) {
79       setDisabled(!disabled);
80       ev.preventDefault();
81     }
82   };
83   return (
84     <div onMouseDown={onMouseDown} >
85       <MenuItem {...{ ...props, disabled: props.disabled && disabled }} />
86     </div>
87   );
88 };
89
90 const mapProps = (state: IApplicationStoreState) => ({
91   networkElementsProperties: createNetworkElementsProperties(state),
92   applicationState: state,
93 });
94
95 const mapDispatch = (dispatcher: IDispatcher) => ({
96   networkElementsActions: createNetworkElementsActions(dispatcher.dispatch),
97   navigateToApplication: (applicationName: string, path?: string) => dispatcher.dispatch(new NavigateToApplication(applicationName, path)),
98   networkElementInfo: async (nodeId: string) => await dispatcher.dispatch(loadAllInfoElementAsync(nodeId)),
99   networkElementFeaturesInfo: async (nodeId: string) => await dispatcher.dispatch(loadAllInfoElementFeaturesAsync(nodeId))
100 });
101
102 type NetworkElementsListComponentProps = WithStyles<typeof styles> & Connect<typeof mapProps, typeof mapDispatch>;
103 type NetworkElementsListComponentState = {
104   networkElementToEdit: NetworkElementConnection,
105   networkElementEditorMode: EditNetworkElementDialogMode,
106   refreshNetworkElementsEditorMode: RefreshNetworkElementsDialogMode,
107   infoNetworkElementEditorMode: InfoNetworkElementDialogMode,
108   elementInfo: TopologyNode | null,
109   elementInfoFeature: ModuleSet | null
110 }
111
112 const emptyRequireNetworkElement: NetworkElementConnection = { id: "", nodeId: "", host: "", port: 0, status: "Disconnected", isRequired: false };
113 let initialSorted = false;
114 const NetworkElementTable = MaterialTable as MaterialTableCtorType<NetworkElementConnection>;
115
116 export class NetworkElementsListComponent extends React.Component<NetworkElementsListComponentProps, NetworkElementsListComponentState> {
117
118   constructor(props: NetworkElementsListComponentProps) {
119     super(props);
120
121     this.state = {
122       networkElementToEdit: emptyRequireNetworkElement,
123       networkElementEditorMode: EditNetworkElementDialogMode.None,
124       refreshNetworkElementsEditorMode: RefreshNetworkElementsDialogMode.None,
125       elementInfo: null,
126       elementInfoFeature: null,
127       infoNetworkElementEditorMode: InfoNetworkElementDialogMode.None
128     };
129   }
130
131   getContextMenu(rowData: NetworkElementConnection): JSX.Element[] {
132     const mountUri = rowData.id && connectService.getNetworkElementUri(rowData.id);
133     const mountPolicy = mountUri && getAccessPolicyByUrl(mountUri);
134     const canMount = mountPolicy && mountPolicy.POST || false;
135
136     const { configuration } = this.props.applicationState as any;
137     const buttonArray = [
138       <MenuItemExt aria-label={"mount-button"} onClick={event => this.onOpenMountdNetworkElementsDialog(event, rowData)} disabled={!canMount} ><LinkIcon /><Typography>Mount</Typography></MenuItemExt>,
139       <MenuItemExt aria-label={"unmount-button"} onClick={event => this.onOpenUnmountdNetworkElementsDialog(event, rowData)} disabled={!canMount} ><LinkOffIcon /><Typography>Unmount</Typography></MenuItemExt>,
140       <Divider />,
141       <MenuItem aria-label={"info-button"} onClick={event => this.onOpenInfoNetworkElementDialog(event, rowData)} disabled={rowData.status === "Connecting" || rowData.status === "Disconnected"} ><Info /><Typography>Info</Typography></MenuItem>,
142       <MenuItem aria-label={"edit-button"} onClick={event => this.onOpenEditNetworkElementDialog(event, rowData)}><EditIcon /><Typography>Edit</Typography></MenuItem>,
143       <MenuItem aria-label={"remove-button"} onClick={event => this.onOpenRemoveNetworkElementDialog(event, rowData)} ><RemoveIcon /><Typography>Remove</Typography></MenuItem>,
144       <Divider />,
145       <MenuItem aria-label={"inventory-button"} onClick={event => this.props.navigateToApplication("inventory", rowData.nodeId)}><Typography>Inventory</Typography></MenuItem>,
146       <Divider />,
147       <MenuItem aria-label={"fault-button"} onClick={event => this.props.navigateToApplication("fault", rowData.nodeId)} ><Typography>Fault</Typography></MenuItem>,
148       <MenuItem aria-label={"configure-button"} onClick={event => this.props.navigateToApplication("configuration", rowData.nodeId)} disabled={rowData.status === "Connecting" || rowData.status === "Disconnected" || !configuration}><Typography>Configure</Typography></MenuItem>,
149       <MenuItem onClick={event => this.props.navigateToApplication("accounting", rowData.nodeId)} disabled={true}><Typography>Accounting</Typography></MenuItem>,
150       <MenuItem aria-label={"performance-button"} onClick={event => this.props.navigateToApplication("performanceHistory", rowData.nodeId)}><Typography>Performance</Typography></MenuItem>,
151       <MenuItem onClick={event => this.props.navigateToApplication("security", rowData.nodeId)} disabled={true} ><Typography>Security</Typography></MenuItem>,
152     ];
153
154     if (rowData.weburi) {
155       // add an icon for gui cuttrough, if weburi is available
156       return [<MenuItem aria-label={"web-client-button"} onClick={event => window.open(rowData.weburi, "_blank")} ><ComputerIcon /><Typography>Web Client</Typography></MenuItem>].concat(buttonArray)
157     } else {
158       return buttonArray;
159     }
160   }
161
162   //  private navigationCreator
163
164   render(): JSX.Element {
165     const { classes } = this.props;
166     const { networkElementToEdit } = this.state;
167     let savedRadio = "password";
168     if (this.state.networkElementToEdit.password && this.state.networkElementToEdit.password.length > 0) {
169       savedRadio = 'password'
170     } else if (this.state.networkElementToEdit.tlsKey && this.state.networkElementToEdit.tlsKey.length > 0) {
171       savedRadio = 'tlsKey'
172     }
173
174     // const mountUri = rowData.id && connectService.getNetworkElementUri(rowData.id);
175     // const mountPolicy = mountUri && getAccessPolicyByUrl(mountUri);
176     // const canAdd =  mountPolicy && mountPolicy.POST || false;
177     const canAdd = true;
178
179     const addRequireNetworkElementAction = {
180       icon: AddIcon, tooltip: 'Add', ariaLabel: "add-element", onClick: () => {
181         this.setState({
182           networkElementEditorMode: EditNetworkElementDialogMode.AddNewNetworkElement,
183           networkElementToEdit: emptyRequireNetworkElement,
184         });
185       }
186     };
187
188     const refreshNetworkElementsAction = {
189       icon: Refresh, tooltip: 'Refresh Network Elements table', ariaLabel: 'refresh', onClick: () => {
190         this.setState({
191           refreshNetworkElementsEditorMode: RefreshNetworkElementsDialogMode.RefreshNetworkElementsTable
192         });
193       }
194     };
195
196     return <>
197       <NetworkElementTable stickyHeader tableId="network-element-table" customActionButtons={[refreshNetworkElementsAction, ...(canAdd ? [addRequireNetworkElementAction] : [])]} columns={[
198         { property: "nodeId", title: "Node Name", type: ColumnType.text },
199         { property: "isRequired", title: "Required", type: ColumnType.boolean },
200         { property: "status", title: "Connection Status", type: ColumnType.text, width:'15%' },
201         { property: "host", title: "Host", type: ColumnType.text },
202         { property: "port", title: "Port", type: ColumnType.numeric },
203         { property: "coreModelCapability", title: "Core Model", type: ColumnType.text },
204         { property: "deviceType", title: "Device Type", type: ColumnType.text },
205         { property: "deviceFunction", title: "Device Function", type: ColumnType.text, width: '15%' }
206       ]} idProperty="id" {...this.props.networkElementsActions} {...this.props.networkElementsProperties} asynchronus createContextMenu={rowData => {
207
208         return this.getContextMenu(rowData);
209       }} >
210       </NetworkElementTable>
211       <EditNetworkElementDialog
212         initialNetworkElement={networkElementToEdit}
213         mode={this.state.networkElementEditorMode}
214         onClose={this.onCloseEditNetworkElementDialog}
215         radioChecked={savedRadio}
216       />
217       <RefreshNetworkElementsDialog
218         mode={this.state.refreshNetworkElementsEditorMode}
219         onClose={this.onCloseRefreshNetworkElementsDialog}
220       />
221       <InfoNetworkElementDialog
222         initialNetworkElement={networkElementToEdit}
223         mode={this.state.infoNetworkElementEditorMode}
224         onClose={this.onCloseInfoNetworkElementDialog}
225       />
226     </>;
227   };
228
229   public componentDidMount() {
230     if (!initialSorted) {
231       initialSorted = true;
232       this.props.networkElementsActions.onHandleRequestSort("node-id");
233     } else {
234       this.props.networkElementsActions.onRefresh();
235     }
236   }
237
238   private onOpenAddNetworkElementDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => {
239     this.setState({
240       networkElementToEdit: element,
241       networkElementEditorMode: EditNetworkElementDialogMode.AddNewNetworkElement
242     });
243   }
244
245   private onOpenRemoveNetworkElementDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => {
246     this.setState({
247       networkElementToEdit: element,
248       networkElementEditorMode: EditNetworkElementDialogMode.RemoveNetworkElement
249     });
250   }
251
252   private onOpenEditNetworkElementDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => {
253     let radioSaved;
254     if (element.password && element.password.length > 0)
255       radioSaved = 'password'
256     else if (element.tlsKey && element.tlsKey.length > 0)
257       radioSaved = 'tlsKey'
258     this.setState({
259       networkElementToEdit: {
260         nodeId: element.nodeId,
261         isRequired: element.isRequired,
262         host: element.host,
263         port: element.port,
264         username: element.username,
265         password: element.password,
266         tlsKey: element.tlsKey
267       },
268       networkElementEditorMode: EditNetworkElementDialogMode.EditNetworkElement
269     });
270   }
271
272   private onOpenUnmountdNetworkElementsDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => {
273     this.setState({
274       networkElementToEdit: element,
275       networkElementEditorMode: EditNetworkElementDialogMode.UnmountNetworkElement
276     });
277   }
278
279   private onOpenMountdNetworkElementsDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => {
280     this.setState({
281       networkElementToEdit: element,
282       networkElementEditorMode: EditNetworkElementDialogMode.MountNetworkElement
283     });
284   }
285
286   private onOpenInfoNetworkElementDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => {
287     this.props.networkElementInfo(element.nodeId);
288     this.props.networkElementFeaturesInfo(element.nodeId);
289     this.setState({
290       networkElementToEdit: element,
291       infoNetworkElementEditorMode: InfoNetworkElementDialogMode.InfoNetworkElement,
292     });
293   }
294
295   private onCloseEditNetworkElementDialog = () => {
296     this.setState({
297       networkElementEditorMode: EditNetworkElementDialogMode.None,
298       networkElementToEdit: emptyRequireNetworkElement,
299     });
300   }
301   private onCloseInfoNetworkElementDialog = () => {
302     this.setState({
303       infoNetworkElementEditorMode: InfoNetworkElementDialogMode.None,
304       networkElementToEdit: emptyRequireNetworkElement,
305     });
306   }
307   private onCloseRefreshNetworkElementsDialog = () => {
308     this.setState({
309       refreshNetworkElementsEditorMode: RefreshNetworkElementsDialogMode.None
310     });
311   }
312 }
313
314 export const NetworkElementsList = withStyles(styles)(connect(mapProps, mapDispatch)(NetworkElementsListComponent));