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 * as React from 'react';
19 import { Theme } from '@mui/material/styles';
21 import { WithStyles } from '@mui/styles';
22 import createStyles from '@mui/styles/createStyles';
23 import withStyles from '@mui/styles/withStyles';
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';
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';
40 import { createNetworkElementsActions, createNetworkElementsProperties } from '../handlers/networkElementsHandler';
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';
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';
52 const styles = (theme: Theme) => createStyles({
53 connectionStatusConnected: {
56 connectionStatusConnecting: {
59 connectionStatusDisconnected: {
68 marginLeft: theme.spacing(1),
69 marginRight: theme.spacing(1),
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);
84 <div onMouseDown={onMouseDown} >
85 <MenuItem {...{ ...props, disabled: props.disabled && disabled }} />
90 const mapProps = (state: IApplicationStoreState) => ({
91 networkElementsProperties: createNetworkElementsProperties(state),
92 applicationState: state,
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))
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
112 const emptyRequireNetworkElement: NetworkElementConnection = { id: "", nodeId: "", host: "", port: 0, status: "Disconnected", isRequired: false };
113 let initialSorted = false;
114 const NetworkElementTable = MaterialTable as MaterialTableCtorType<NetworkElementConnection>;
116 export class NetworkElementsListComponent extends React.Component<NetworkElementsListComponentProps, NetworkElementsListComponentState> {
118 constructor(props: NetworkElementsListComponentProps) {
122 networkElementToEdit: emptyRequireNetworkElement,
123 networkElementEditorMode: EditNetworkElementDialogMode.None,
124 refreshNetworkElementsEditorMode: RefreshNetworkElementsDialogMode.None,
126 elementInfoFeature: null,
127 infoNetworkElementEditorMode: InfoNetworkElementDialogMode.None
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;
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>,
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>,
145 <MenuItem aria-label={"inventory-button"} onClick={event => this.props.navigateToApplication("inventory", rowData.nodeId)}><Typography>Inventory</Typography></MenuItem>,
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>,
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)
162 // private navigationCreator
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'
174 // const mountUri = rowData.id && connectService.getNetworkElementUri(rowData.id);
175 // const mountPolicy = mountUri && getAccessPolicyByUrl(mountUri);
176 // const canAdd = mountPolicy && mountPolicy.POST || false;
179 const addRequireNetworkElementAction = {
180 icon: AddIcon, tooltip: 'Add', ariaLabel: "add-element", onClick: () => {
182 networkElementEditorMode: EditNetworkElementDialogMode.AddNewNetworkElement,
183 networkElementToEdit: emptyRequireNetworkElement,
188 const refreshNetworkElementsAction = {
189 icon: Refresh, tooltip: 'Refresh Network Elements table', ariaLabel: 'refresh', onClick: () => {
191 refreshNetworkElementsEditorMode: RefreshNetworkElementsDialogMode.RefreshNetworkElementsTable
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 => {
208 return this.getContextMenu(rowData);
210 </NetworkElementTable>
211 <EditNetworkElementDialog
212 initialNetworkElement={networkElementToEdit}
213 mode={this.state.networkElementEditorMode}
214 onClose={this.onCloseEditNetworkElementDialog}
215 radioChecked={savedRadio}
217 <RefreshNetworkElementsDialog
218 mode={this.state.refreshNetworkElementsEditorMode}
219 onClose={this.onCloseRefreshNetworkElementsDialog}
221 <InfoNetworkElementDialog
222 initialNetworkElement={networkElementToEdit}
223 mode={this.state.infoNetworkElementEditorMode}
224 onClose={this.onCloseInfoNetworkElementDialog}
229 public componentDidMount() {
230 if (!initialSorted) {
231 initialSorted = true;
232 this.props.networkElementsActions.onHandleRequestSort("node-id");
234 this.props.networkElementsActions.onRefresh();
238 private onOpenAddNetworkElementDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => {
240 networkElementToEdit: element,
241 networkElementEditorMode: EditNetworkElementDialogMode.AddNewNetworkElement
245 private onOpenRemoveNetworkElementDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => {
247 networkElementToEdit: element,
248 networkElementEditorMode: EditNetworkElementDialogMode.RemoveNetworkElement
252 private onOpenEditNetworkElementDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => {
254 if (element.password && element.password.length > 0)
255 radioSaved = 'password'
256 else if (element.tlsKey && element.tlsKey.length > 0)
257 radioSaved = 'tlsKey'
259 networkElementToEdit: {
260 nodeId: element.nodeId,
261 isRequired: element.isRequired,
264 username: element.username,
265 password: element.password,
266 tlsKey: element.tlsKey
268 networkElementEditorMode: EditNetworkElementDialogMode.EditNetworkElement
272 private onOpenUnmountdNetworkElementsDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => {
274 networkElementToEdit: element,
275 networkElementEditorMode: EditNetworkElementDialogMode.UnmountNetworkElement
279 private onOpenMountdNetworkElementsDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => {
281 networkElementToEdit: element,
282 networkElementEditorMode: EditNetworkElementDialogMode.MountNetworkElement
286 private onOpenInfoNetworkElementDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => {
287 this.props.networkElementInfo(element.nodeId);
288 this.props.networkElementFeaturesInfo(element.nodeId);
290 networkElementToEdit: element,
291 infoNetworkElementEditorMode: InfoNetworkElementDialogMode.InfoNetworkElement,
295 private onCloseEditNetworkElementDialog = () => {
297 networkElementEditorMode: EditNetworkElementDialogMode.None,
298 networkElementToEdit: emptyRequireNetworkElement,
301 private onCloseInfoNetworkElementDialog = () => {
303 infoNetworkElementEditorMode: InfoNetworkElementDialogMode.None,
304 networkElementToEdit: emptyRequireNetworkElement,
307 private onCloseRefreshNetworkElementsDialog = () => {
309 refreshNetworkElementsEditorMode: RefreshNetworkElementsDialogMode.None
314 export const NetworkElementsList = withStyles(styles)(connect(mapProps, mapDispatch)(NetworkElementsListComponent));