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
10 * http://www.apache.org/licenses/LICENSE-2.0
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
16 * ============LICENSE_END==========================================================================
18 import React from 'react';
20 import AddIcon from '@mui/icons-material/Add';
21 import ComputerIcon from '@mui/icons-material/Computer';
22 import EditIcon from '@mui/icons-material/Edit';
23 import Info from '@mui/icons-material/Info';
24 import LinkIcon from '@mui/icons-material/Link';
25 import LinkOffIcon from '@mui/icons-material/LinkOff';
26 import Refresh from '@mui/icons-material/Refresh';
27 import RemoveIcon from '@mui/icons-material/RemoveCircleOutline';
28 import { Divider, MenuItem, Typography } from '@mui/material';
29 import { Theme } from '@mui/material/styles';
30 import { WithStyles } from '@mui/styles';
31 import createStyles from '@mui/styles/createStyles';
32 import withStyles from '@mui/styles/withStyles';
34 import { NavigateToApplication } from '../../../../framework/src/actions/navigationActions';
35 import { ColumnType, MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table';
36 import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect';
37 import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
38 import { getAccessPolicyByUrl } from '../../../../framework/src/services/restService';
40 import { loadAllInfoElementAsync, loadAllInfoElementFeaturesAsync } from '../actions/infoNetworkElementActions';
41 import { createNetworkElementsActions, createNetworkElementsProperties } from '../handlers/networkElementsHandler';
42 import { NetworkElementConnection } from '../models/networkElementConnection';
43 import { ModuleSet, TopologyNode } from '../models/topologyNetconf';
44 import { connectService } from '../services/connectService';
46 import EditNetworkElementDialog, { EditNetworkElementDialogMode } from './editNetworkElementDialog';
47 import InfoNetworkElementDialog, { InfoNetworkElementDialogMode } from './infoNetworkElementDialog';
48 import RefreshNetworkElementsDialog, { RefreshNetworkElementsDialogMode } from './refreshNetworkElementsDialog';
50 const styles = (theme: Theme) => createStyles({
51 connectionStatusConnected: {
54 connectionStatusConnecting: {
57 connectionStatusDisconnected: {
66 marginLeft: theme.spacing(1),
67 marginRight: theme.spacing(1),
72 type GetStatelessComponentProps<T> = T extends (props: infer P & { children?: React.ReactNode }) => any ? P : any;
73 const MenuItemExt: React.FC<GetStatelessComponentProps<typeof MenuItem>> = (props) => {
74 const [disabled, setDisabled] = React.useState(true);
75 const onMouseDown = (ev: React.MouseEvent<HTMLElement>) => {
76 if (ev.button === 1) {
77 setDisabled(!disabled);
82 <div onMouseDown={onMouseDown} >
83 <MenuItem {...{ ...props, disabled: props.disabled && disabled }} />
88 const mapProps = (state: IApplicationStoreState) => ({
89 networkElementsProperties: createNetworkElementsProperties(state),
90 applicationState: state,
93 const mapDispatch = (dispatcher: IDispatcher) => ({
94 networkElementsActions: createNetworkElementsActions(dispatcher.dispatch),
95 navigateToApplication: (applicationName: string, path?: string) => dispatcher.dispatch(new NavigateToApplication(applicationName, path)),
96 networkElementInfo: async (nodeId: string) => dispatcher.dispatch(loadAllInfoElementAsync(nodeId)),
97 networkElementFeaturesInfo: async (nodeId: string) => dispatcher.dispatch(loadAllInfoElementFeaturesAsync(nodeId)),
100 type NetworkElementsListComponentProps = WithStyles<typeof styles> & Connect<typeof mapProps, typeof mapDispatch>;
101 type NetworkElementsListComponentState = {
102 networkElementToEdit: NetworkElementConnection;
103 networkElementEditorMode: EditNetworkElementDialogMode;
104 refreshNetworkElementsEditorMode: RefreshNetworkElementsDialogMode;
105 infoNetworkElementEditorMode: InfoNetworkElementDialogMode;
106 elementInfo: TopologyNode | null;
107 elementInfoFeature: ModuleSet | null;
110 const emptyRequireNetworkElement: NetworkElementConnection = { id: '', nodeId: '', host: '', port: 830, status: 'Disconnected', isRequired: true };
111 let initialSorted = false;
112 const NetworkElementTable = MaterialTable as MaterialTableCtorType<NetworkElementConnection>;
114 export class NetworkElementsListComponent extends React.Component<NetworkElementsListComponentProps, NetworkElementsListComponentState> {
116 constructor(props: NetworkElementsListComponentProps) {
120 networkElementToEdit: emptyRequireNetworkElement,
121 networkElementEditorMode: EditNetworkElementDialogMode.None,
122 refreshNetworkElementsEditorMode: RefreshNetworkElementsDialogMode.None,
124 elementInfoFeature: null,
125 infoNetworkElementEditorMode: InfoNetworkElementDialogMode.None,
129 getContextMenu(rowData: NetworkElementConnection): JSX.Element[] {
130 const mountUri = rowData.id && connectService.getNetworkElementUri(rowData.id);
131 const mountPolicy = mountUri && getAccessPolicyByUrl(mountUri);
132 const canMount = mountPolicy && mountPolicy.POST || false;
134 const { configuration } = this.props.applicationState as any;
135 const buttonArray = [
136 <MenuItemExt aria-label={'mount-button'} onClick={event => this.onOpenMountdNetworkElementsDialog(event, rowData)} disabled={!canMount} ><LinkIcon /><Typography>Mount</Typography></MenuItemExt>,
137 <MenuItemExt aria-label={'unmount-button'} onClick={event => this.onOpenUnmountdNetworkElementsDialog(event, rowData)} disabled={!canMount} ><LinkOffIcon /><Typography>Unmount</Typography></MenuItemExt>,
139 <MenuItem aria-label={'info-button'} onClick={event => this.onOpenInfoNetworkElementDialog(event, rowData)} disabled={rowData.status !== 'Connected'} ><Info /><Typography>Info</Typography></MenuItem>,
140 <MenuItem aria-label={'edit-button'} onClick={event => this.onOpenEditNetworkElementDialog(event, rowData)}><EditIcon /><Typography>Edit</Typography></MenuItem>,
141 <MenuItem aria-label={'remove-button'} onClick={event => this.onOpenRemoveNetworkElementDialog(event, rowData)} ><RemoveIcon /><Typography>Remove</Typography></MenuItem>,
143 <MenuItem aria-label={'inventory-button'} onClick={() => this.props.navigateToApplication('inventory', rowData.nodeId)}><Typography>Inventory</Typography></MenuItem>,
145 <MenuItem aria-label={'fault-button'} onClick={() => this.props.navigateToApplication('fault', rowData.nodeId)} ><Typography>Fault</Typography></MenuItem>,
146 <MenuItem aria-label={'configure-button'} onClick={() => this.props.navigateToApplication('configuration', rowData.nodeId)} disabled={rowData.status === 'Connecting' || rowData.status === 'Disconnected' || !configuration}><Typography>Configure</Typography></MenuItem>,
147 <MenuItem onClick={() => this.props.navigateToApplication('accounting', rowData.nodeId)} disabled={true}><Typography>Accounting</Typography></MenuItem>,
148 <MenuItem aria-label={'performance-button'} onClick={() => this.props.navigateToApplication('performanceHistory', rowData.nodeId)}><Typography>Performance</Typography></MenuItem>,
149 <MenuItem onClick={() => this.props.navigateToApplication('security', rowData.nodeId)} disabled={true} ><Typography>Security</Typography></MenuItem>,
152 if (rowData.weburi) {
153 // add an icon for gui cuttrough, if weburi is available
154 return [<MenuItem aria-label={'web-client-button'} onClick={() => window.open(rowData.weburi, '_blank')} ><ComputerIcon /><Typography>Web Client</Typography></MenuItem>].concat(buttonArray);
160 // private navigationCreator
162 render(): JSX.Element {
163 //const { classes } = this.props;
164 const { networkElementToEdit } = this.state;
165 let savedRadio = 'password';
166 if (this.state.networkElementToEdit.password && this.state.networkElementToEdit.password.length > 0) {
167 savedRadio = 'password';
168 } else if (this.state.networkElementToEdit.tlsKey && this.state.networkElementToEdit.tlsKey.length > 0) {
169 savedRadio = 'tlsKey';
172 // const mountUri = rowData.id && connectService.getNetworkElementUri(rowData.id);
173 // const mountPolicy = mountUri && getAccessPolicyByUrl(mountUri);
174 // const canAdd = mountPolicy && mountPolicy.POST || false;
177 const addRequireNetworkElementAction = {
178 icon: AddIcon, tooltip: 'Add node', ariaLabel: 'add-element', onClick: () => {
180 networkElementEditorMode: EditNetworkElementDialogMode.AddNewNetworkElement,
181 networkElementToEdit: emptyRequireNetworkElement,
186 const refreshNetworkElementsAction = {
187 icon: Refresh, tooltip: 'Refresh table', ariaLabel: 'refresh', onClick: () => {
189 refreshNetworkElementsEditorMode: RefreshNetworkElementsDialogMode.RefreshNetworkElementsTable,
195 <NetworkElementTable stickyHeader tableId="network-element-table" customActionButtons={[refreshNetworkElementsAction, ...(canAdd ? [addRequireNetworkElementAction] : [])]} columns={[
196 { property: 'nodeId', title: 'Node ID', type: ColumnType.text },
197 { property: 'status', title: 'Connection Status', type: ColumnType.text, width:'15%' },
198 { property: 'host', title: 'Host', type: ColumnType.text },
199 { property: 'port', title: 'Port', type: ColumnType.numeric },
200 { property: 'isRequired', title: 'Required', type: ColumnType.boolean },
201 { property: 'deviceType', title: 'Type', type: ColumnType.text },
202 // { property: "coreModelCapability", title: "Core Model", type: ColumnType.text },
203 { property: 'deviceFunction', title: 'Function', type: ColumnType.text, width: '25%' },
204 ]} idProperty="id" {...this.props.networkElementsActions} {...this.props.networkElementsProperties} asynchronus createContextMenu={rowData => {
206 return this.getContextMenu(rowData);
208 </NetworkElementTable>
209 <EditNetworkElementDialog
210 initialNetworkElement={networkElementToEdit}
211 mode={this.state.networkElementEditorMode}
212 onClose={this.onCloseEditNetworkElementDialog}
213 radioChecked={savedRadio}
215 <RefreshNetworkElementsDialog
216 mode={this.state.refreshNetworkElementsEditorMode}
217 onClose={this.onCloseRefreshNetworkElementsDialog}
219 <InfoNetworkElementDialog
220 initialNetworkElement={networkElementToEdit}
221 mode={this.state.infoNetworkElementEditorMode}
222 onClose={this.onCloseInfoNetworkElementDialog}
227 public componentDidMount() {
228 if (!initialSorted) {
229 initialSorted = true;
230 this.props.networkElementsActions.onHandleRequestSort('node-id');
232 this.props.networkElementsActions.onRefresh();
236 private onOpenAddNetworkElementDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => {
238 networkElementToEdit: element,
239 networkElementEditorMode: EditNetworkElementDialogMode.AddNewNetworkElement,
243 private onOpenRemoveNetworkElementDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => {
245 networkElementToEdit: element,
246 networkElementEditorMode: EditNetworkElementDialogMode.RemoveNetworkElement,
250 private onOpenEditNetworkElementDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => {
252 //if (element.password && element.password.length > 0)
253 // radioSaved = 'password';
254 //else if (element.tlsKey && element.tlsKey.length > 0)
255 // radioSaved = 'tlsKey';
257 networkElementToEdit: {
258 nodeId: element.nodeId,
259 isRequired: element.isRequired,
262 username: element.username,
263 password: element.password,
264 tlsKey: element.tlsKey,
266 networkElementEditorMode: EditNetworkElementDialogMode.EditNetworkElement,
270 private onOpenUnmountdNetworkElementsDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => {
272 networkElementToEdit: element,
273 networkElementEditorMode: EditNetworkElementDialogMode.UnmountNetworkElement,
277 private onOpenMountdNetworkElementsDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => {
279 networkElementToEdit: element,
280 networkElementEditorMode: EditNetworkElementDialogMode.MountNetworkElement,
284 private onOpenInfoNetworkElementDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => {
285 this.props.networkElementInfo(element.nodeId);
286 this.props.networkElementFeaturesInfo(element.nodeId);
288 networkElementToEdit: element,
289 infoNetworkElementEditorMode: InfoNetworkElementDialogMode.InfoNetworkElement,
293 private onCloseEditNetworkElementDialog = () => {
295 networkElementEditorMode: EditNetworkElementDialogMode.None,
296 networkElementToEdit: emptyRequireNetworkElement,
300 private onCloseInfoNetworkElementDialog = () => {
302 infoNetworkElementEditorMode: InfoNetworkElementDialogMode.None,
303 networkElementToEdit: emptyRequireNetworkElement,
307 private onCloseRefreshNetworkElementsDialog = () => {
309 refreshNetworkElementsEditorMode: RefreshNetworkElementsDialogMode.None,
314 export const NetworkElementsList = withStyles(styles)(connect(mapProps, mapDispatch)(NetworkElementsListComponent));