diff options
Diffstat (limited to 'sdnr/wt/odlux/apps/inventoryApp')
18 files changed, 671 insertions, 113 deletions
diff --git a/sdnr/wt/odlux/apps/inventoryApp/package.json b/sdnr/wt/odlux/apps/inventoryApp/package.json index 929ca0fb4..4b1f3f9f9 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/package.json +++ b/sdnr/wt/odlux/apps/inventoryApp/package.json @@ -24,8 +24,8 @@ "@odlux/framework": "*" }, "peerDependencies": { - "@types/react": "16.9.11", - "@types/react-dom": "16.9.4", + "@types/react": "16.9.19", + "@types/react-dom": "16.9.5", "@types/react-router-dom": "4.3.1", "@material-ui/core": "4.9.0", "@material-ui/icons": "4.5.1", diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/actions/inventoryTreeActions.ts b/sdnr/wt/odlux/apps/inventoryApp/src/actions/inventoryTreeActions.ts new file mode 100644 index 000000000..c09b669a1 --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/src/actions/inventoryTreeActions.ts @@ -0,0 +1,104 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + +import { Action } from '../../../../framework/src/flux/action'; +import { Dispatch } from '../../../../framework/src/flux/store'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; + +import { InventoryType, InventoryTreeNode, TreeDemoItem } from '../models/inventory'; +import { inventoryService } from '../services/inventoryService'; +import { AddErrorInfoAction } from '../../../../framework/src/actions/errorActions'; +import { NavigateToApplication } from '../../../../framework/src/actions/navigationActions'; + +/** + * Represents the base action. + */ +export class BaseAction extends Action { } + +export class SetBusyAction extends BaseAction { + constructor(public busy: boolean = true) { + super(); + + } +} + +export class SetSearchTextAction extends BaseAction { + constructor(public searchTerm: string = "") { + super(); + + } +} + +export class UpdateInventoryTreeAction extends BaseAction { + constructor(public rootNode: InventoryTreeNode) { + super(); + + } +} + +export class UpdateSelectedNodeAction extends BaseAction { + constructor(public selectedNode?: InventoryType) { + super(); + + } +} + +export class UpdateExpandedNodesAction extends BaseAction { + constructor(public expandedNodes?: TreeDemoItem[]) { + super(); + + } +} + +export const setSearchTermAction = (searchTerm: string) => (dispatch: Dispatch, getState: () => IApplicationStoreState) =>{ + dispatch(new SetSearchTextAction(searchTerm)); +} + + +export const updateInventoryTreeAsyncAction = (mountId: string, searchTerm?: string) => async (dispatch: Dispatch, getState: () => IApplicationStoreState) => { + dispatch(new SetBusyAction(true)); + dispatch(new SetSearchTextAction(searchTerm)); + try { + const result = await inventoryService.getInventoryTree(mountId, searchTerm); + if (!result) { + dispatch(new AddErrorInfoAction({ title: "Error", message: `Could not load inventory tree for [${mountId}]. Please check you connection to the server and try later.` })); + dispatch(new NavigateToApplication("inventory")); + } else { + dispatch(new UpdateInventoryTreeAction(result)); + } + } catch (err) { + throw new Error("Could not load inventory tree from server."); + } + finally { + dispatch(new SetBusyAction(false)); + } +}; + +export const selectInventoryNodeAsyncAction = (nodeId: string) => async (dispatch: Dispatch, getState: () => IApplicationStoreState) => { + dispatch(new SetBusyAction(true)); + try { + const result = await inventoryService.getInventoryEntry(nodeId); + if (!result) throw new Error("Could not load inventory tree from server."); + dispatch(new UpdateSelectedNodeAction(result)); + } catch (err) { + throw new Error("Could not load inventory tree from server."); + } + finally { + dispatch(new SetBusyAction(false)); + } +}; diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/actions/panelActions.ts b/sdnr/wt/odlux/apps/inventoryApp/src/actions/panelActions.ts new file mode 100644 index 000000000..10fde8c1d --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/src/actions/panelActions.ts @@ -0,0 +1,31 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + +import { Action } from "../../../../framework/src/flux/action"; +import { PanelId } from "models/panelId"; + + +export class SetPanelAction extends Action { + constructor(public panelId: PanelId) { + super(); + } + } + +export const setPanelAction = (panelId: PanelId) => { + return new SetPanelAction(panelId); + }
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/fakeData/index.ts b/sdnr/wt/odlux/apps/inventoryApp/src/fakeData/index.ts index 692ea82c7..46827e842 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/fakeData/index.ts +++ b/sdnr/wt/odlux/apps/inventoryApp/src/fakeData/index.ts @@ -1,3 +1,21 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + import { InventoryTreeNode, InventoryType } from "models/inventory"; import { convertPropertyNames, replaceHyphen } from "../../../../framework/src/utilities/yangHelper"; @@ -30,31 +48,31 @@ const deleay = (time: number) => () => new Promise<number>(resolve => setTimeout const getTreeElements = (searchTerm: string | null, treeLevel: number = 0, parentUUID: string | null = null): [InventoryTreeNode, boolean] => { const elements = (data.filter(e => e["tree-level"] === treeLevel && (!parentUUID || e["parent-uuid"] === parentUUID)) || []) let elementMatch = false; - const treeeNode = elements.reduce<InventoryTreeNode>((acc, cur) => { - const [children, childMatch] = getTreeElements(searchTerm, treeLevel + 1, cur["node-id"]); - const isMatch = searchTerm ? Object.keys(cur).some(k => String((cur as any)[k]).indexOf(searchTerm)) : false; + const treeNode = elements.reduce<InventoryTreeNode>((acc, cur) => { + const [children, childMatch] = getTreeElements(searchTerm, treeLevel + 1, cur["uuid"]); + const isMatch = searchTerm ? Object.keys(cur).some(k => String((cur as any)[k]).indexOf(searchTerm) > -1) : false; elementMatch = elementMatch || isMatch || childMatch; if (!searchTerm || isMatch || childMatch) { - acc[cur["node-id"]] = { - label: cur["node-id"], + acc[cur["uuid"]] = { + label: cur["uuid"], children: children, - isMatch: false, + isMatch: isMatch, }; } return acc; }, {}); - return [treeeNode, elementMatch] + return [treeNode, elementMatch] }; -export const getTree = async (searchTerm: string | null = null) : Promise<InventoryTreeNode> => { +export const getTree = async (searchTerm: string | null = null): Promise<InventoryTreeNode> => { await deleay(600); const [node] = getTreeElements(searchTerm); return node; }; -export const getElement = async (id: string ): Promise<InventoryType | undefined> => { +export const getElement = async (id: string): Promise<InventoryType | undefined> => { await deleay(600); - const res = data.find(e => e.id === id); + const res = data.find(e => e.uuid === id); return res && convertPropertyNames(res, replaceHyphen) as unknown as InventoryType; }; diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/handlers/connectedNetworkElementsHandler.ts b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/connectedNetworkElementsHandler.ts new file mode 100644 index 000000000..79c12d619 --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/connectedNetworkElementsHandler.ts @@ -0,0 +1,36 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + +import { createExternal, IExternalTableState } from '../../../../framework/src/components/material-table/utilities'; +import { createSearchDataHandler } from '../../../../framework/src/utilities/elasticSearch'; + +import { NetworkElementConnection } from '../models/networkElementConnection'; + +export interface IConnectedNetworkElementsState extends IExternalTableState<NetworkElementConnection> { } + +// create eleactic search material data fetch handler +const connectedNetworkElementsSearchHandler = createSearchDataHandler<NetworkElementConnection>('network-element-connection', { status: "Connected" }); + +export const { + actionHandler: connectedNetworkElementsActionHandler, + createActions: createConnectedNetworkElementsActions, + createProperties: createConnectedNetworkElementsProperties, + reloadAction: connectedNetworkElementsReloadAction, + + // set value action, to change a value +} = createExternal<NetworkElementConnection>(connectedNetworkElementsSearchHandler, appState => appState.inventory.connectedNetworkElements); diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryAppRootHandler.ts b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryAppRootHandler.ts index 786f6d0c5..0e857ffe9 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryAppRootHandler.ts +++ b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryAppRootHandler.ts @@ -22,14 +22,23 @@ import { combineActionHandler } from '../../../../framework/src/flux/middleware' // ** do not remove ** import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; import { IActionHandler } from '../../../../framework/src/flux/action'; -import { IInventoryElementsState, inventoryElementsActionHandler } from './inventoryElementsHandler'; +import { IInvenroryTree, inventoryTreeHandler } from './inventoryTreeHandler'; +import { IConnectedNetworkElementsState, connectedNetworkElementsActionHandler } from './connectedNetworkElementsHandler'; +import { PanelId } from '../models/panelId'; +import { currentOpenPanelHandler } from './panelHandler'; +import { inventoryElementsActionHandler, IInventoryElementsState } from './inventoryElementsHandler'; export interface IInventoryAppStateState { - inventoryElements: IInventoryElementsState + inventoryTree: IInvenroryTree; + connectedNetworkElements: IConnectedNetworkElementsState; // used for ne selection + currentOpenPanel: PanelId; + inventoryElements: IInventoryElementsState; } + + declare module '../../../../framework/src/store/applicationStore' { interface IApplicationStoreState { inventory: IInventoryAppStateState; @@ -37,6 +46,9 @@ declare module '../../../../framework/src/store/applicationStore' { } const actionHandlers = { + inventoryTree: inventoryTreeHandler, + connectedNetworkElements: connectedNetworkElementsActionHandler, + currentOpenPanel: currentOpenPanelHandler, inventoryElements: inventoryElementsActionHandler }; diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryElementsHandler.tsx b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryElementsHandler.ts index a65319efa..a65319efa 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryElementsHandler.tsx +++ b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryElementsHandler.ts diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryTreeHandler.ts b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryTreeHandler.ts new file mode 100644 index 000000000..9029a6719 --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryTreeHandler.ts @@ -0,0 +1,68 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + +import { IActionHandler } from "../../../../framework/src/flux/action"; + +import { SetBusyAction, UpdateInventoryTreeAction, UpdateSelectedNodeAction, SetSearchTextAction, UpdateExpandedNodesAction } from "../actions/inventoryTreeActions"; +import { InventoryTreeNode, InventoryType, TreeDemoItem } from "../models/inventory"; + + +export interface IInvenroryTree { + isBusy: boolean; + rootNodes: TreeDemoItem[]; + selectedNode?: InventoryType; + expandedItems: TreeDemoItem[]; + searchTerm: string; +} + +const initialState: IInvenroryTree = { + isBusy: false, + rootNodes: [], + searchTerm: "", + selectedNode: undefined, + expandedItems: [], +} + + +const getTreeDataFromInvetoryTreeNode = (node: InventoryTreeNode): TreeDemoItem[] => Object.keys(node).reduce<TreeDemoItem[]>((acc, key) => { + const cur = node[key]; + acc.push({ + isMatch: cur.isMatch, + content: cur.label || key, + value: key, + children: cur.children && getTreeDataFromInvetoryTreeNode(cur.children), + }); + return acc; +}, []); + +export const inventoryTreeHandler: IActionHandler<IInvenroryTree> = (state = initialState, action) => { + if (action instanceof SetBusyAction) { + state = { ...state, isBusy: action.busy }; + } else if (action instanceof SetSearchTextAction) { + state = { ...state, searchTerm: action.searchTerm }; + } else if (action instanceof UpdateInventoryTreeAction) { + const rootNodes = getTreeDataFromInvetoryTreeNode(action.rootNode); + state = { ...state, rootNodes: rootNodes, expandedItems: [], selectedNode: undefined }; + } else if (action instanceof UpdateSelectedNodeAction) { + state = { ...state, selectedNode: action.selectedNode }; + } else if (action instanceof UpdateExpandedNodesAction) { + state = { ...state, expandedItems: action.expandedNodes || []} + } + + return state; +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/handlers/panelHandler.ts b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/panelHandler.ts new file mode 100644 index 000000000..761253112 --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/panelHandler.ts @@ -0,0 +1,11 @@ +import { PanelId } from "../models/panelId"; +import { IActionHandler } from "../../../../framework/src/flux/action"; +import { SetPanelAction } from "../actions/panelActions"; + + +export const currentOpenPanelHandler: IActionHandler<PanelId> = (state = null, action) => { + if (action instanceof SetPanelAction) { + state = action.panelId; + } + return state; + }
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/index.html b/sdnr/wt/odlux/apps/inventoryApp/src/index.html index 0cdb9e93b..75531ec1b 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/index.html +++ b/sdnr/wt/odlux/apps/inventoryApp/src/index.html @@ -15,8 +15,9 @@ <script type="text/javascript" src="./config.js"></script> <script> // run the application - require(["app", "inventoryApp"], function (app, inventoryApp) { + require(["app", "inventoryApp", "connectApp"], function (app, inventoryApp, connectApp) { inventoryApp.register(); + connectApp.register(); app("./app.tsx").runApplication(); }); </script> diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/models/inventory.ts b/sdnr/wt/odlux/apps/inventoryApp/src/models/inventory.ts index c6b6c91cb..a6c990529 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/models/inventory.ts +++ b/sdnr/wt/odlux/apps/inventoryApp/src/models/inventory.ts @@ -15,6 +15,9 @@ * the License. * ============LICENSE_END========================================================================== */ + +import { ExternalTreeItem } from '../../../../framework/src/components/material-ui/treeView'; + export { HitEntry, Result } from '../../../../framework/src/models'; export type InventoryType = { @@ -23,7 +26,7 @@ export type InventoryType = { nodeId: string; uuid: string; containedHolder?: (string)[] | null; - manufacturerName?: string ; + manufacturerName?: string; manufacturerIdentifier: string; serial: string; date: string; @@ -42,4 +45,6 @@ export type InventoryTreeNode = { ownSeverity?: string; childrenSeveritySummary?: string; } -}
\ No newline at end of file +} + +export type TreeDemoItem = ExternalTreeItem<string>;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/models/networkElementConnection.ts b/sdnr/wt/odlux/apps/inventoryApp/src/models/networkElementConnection.ts new file mode 100644 index 000000000..88f70181c --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/src/models/networkElementConnection.ts @@ -0,0 +1,37 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + +export type NetworkElementConnection = { + id?: string; + nodeId: string; + host: string; + port: number; + username?: string; + password?: string; + isRequired?: boolean; + status?: "connected" | "mounted" | "unmounted" | "connecting" | "disconnected" | "idle"; + coreModelCapability?: string; + deviceType?: string; + nodeDetails?: { + availableCapabilities: string[]; + unavailableCapabilities: { + failureReason: string; + capability: string; + }[]; + } +} diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/models/panelId.ts b/sdnr/wt/odlux/apps/inventoryApp/src/models/panelId.ts new file mode 100644 index 000000000..b05c1db64 --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/src/models/panelId.ts @@ -0,0 +1,19 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + +export type PanelId = null | "InventoryElementsTable" | "TreeviewTable";
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/pluginInventory.tsx b/sdnr/wt/odlux/apps/inventoryApp/src/pluginInventory.tsx index ad53285cb..665e085e6 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/pluginInventory.tsx +++ b/sdnr/wt/odlux/apps/inventoryApp/src/pluginInventory.tsx @@ -22,43 +22,15 @@ import { withRouter, RouteComponentProps, Route, Switch, Redirect } from 'react- import { faShoppingBag } from '@fortawesome/free-solid-svg-icons'; // select app icon import applicationManager from '../../../framework/src/services/applicationManager'; -import connect, { Connect, IDispatcher } from '../../../framework/src/flux/connect'; -import { IApplicationStoreState } from "../../../framework/src/store/applicationStore"; +import { InventoryTreeView } from './views/treeview'; +import Dashboard from "./views/dashboard"; -import { Dashboard } from './views/dashboard'; import inventoryAppRootHandler from './handlers/inventoryAppRootHandler'; -import { createInventoryElementsProperties, createInventoryElementsActions, inventoryElementsReloadAction } from "./handlers/inventoryElementsHandler"; - -let currentMountId: string | undefined = undefined; - -const mapProps = (state: IApplicationStoreState) => ({ - inventoryProperties: createInventoryElementsProperties(state), -}); - -const mapDisp = (dispatcher: IDispatcher) => ({ - inventoryActions: createInventoryElementsActions(dispatcher.dispatch, true) -}); - -const InventoryApplicationRouteAdapter = connect(mapProps, mapDisp)((props: RouteComponentProps<{ mountId?: string }> & Connect<typeof mapProps, typeof mapDisp>) => { - if (currentMountId !== props.match.params.mountId) { - currentMountId = props.match.params.mountId || undefined; - window.setTimeout(() => { - if (currentMountId) { - props.inventoryActions.onFilterChanged("nodeId", currentMountId); - props.inventoryProperties.showFilter; - props.inventoryActions.onRefresh(); - } - }); - } - return ( - <Dashboard /> - ) -}); - const App = withRouter((props: RouteComponentProps) => ( <Switch> - <Route path={`${props.match.path}/:mountId?`} component={InventoryApplicationRouteAdapter} /> + <Route path={`${props.match.path}/:mountId`} component={InventoryTreeView} /> + <Route path={`${props.match.path}`} component={Dashboard} /> <Redirect to={`${props.match.path}`} /> </Switch> )); diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/services/inventoryService.ts b/sdnr/wt/odlux/apps/inventoryApp/src/services/inventoryService.ts index 252d6d425..b6025d4da 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/services/inventoryService.ts +++ b/sdnr/wt/odlux/apps/inventoryApp/src/services/inventoryService.ts @@ -16,6 +16,7 @@ * ============LICENSE_END========================================================================== */ import { requestRest } from '../../../../framework/src/services/restService'; +import { convertPropertyNames, replaceHyphen } from '../../../../framework/src/utilities/yangHelper'; import { InventoryTreeNode, InventoryType } from '../models/inventory'; import { getTree, getElement } from '../fakeData'; @@ -24,12 +25,44 @@ import { getTree, getElement } from '../fakeData'; * Represents a web api accessor service for all maintenence entries related actions. */ class InventoryService { - public async getInventoryTree(searchTerm?: string): Promise<InventoryTreeNode> { - return await getTree(searchTerm); + public async getInventoryTree(mountId: string, searchTerm: string = ""): Promise<InventoryTreeNode | null> { + //return await getTree(searchTerm); + const path = `/tree/read-inventoryequipment-tree/${mountId}`; + const body = { + "query": searchTerm + }; + const inventoryTree = await requestRest<InventoryTreeNode>(path, { method: "POST" , body: JSON.stringify(body)}); + return inventoryTree && convertPropertyNames(inventoryTree, replaceHyphen) || null; } - public async getInventoryEntry(id: string): Promise<InventoryType| undefined> { - return await getElement(id); + public async getInventoryEntry(id: string): Promise<InventoryType | undefined> { + const path = `/restconf/operations/data-provider:read-inventory-list`; + const body = { + "input": { + "filter": [ + { property: "id", filtervalue: id }, + ], + "sortorder": [], + "pagination": { + "size": 1, + "page": 1 + } + } + }; + const inventoryTreeElement = await requestRest<{ + output: { + "pagination": { + "size": number, + "page": number, + "total": number + }, + "data": InventoryType[] + } + }>(path, { method: "POST", body: JSON.stringify(body) }); + + return inventoryTreeElement && inventoryTreeElement.output && inventoryTreeElement.output.pagination && inventoryTreeElement.output.pagination.total >= 1 && + inventoryTreeElement.output.data && convertPropertyNames(inventoryTreeElement.output.data[0], replaceHyphen) || undefined; + // return await getElement(id); } } diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/views/dashboard.tsx b/sdnr/wt/odlux/apps/inventoryApp/src/views/dashboard.tsx index b63f628a3..14792df5b 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/views/dashboard.tsx +++ b/sdnr/wt/odlux/apps/inventoryApp/src/views/dashboard.tsx @@ -15,88 +15,163 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from "react"; -import { withStyles, WithStyles, createStyles, Theme } from '@material-ui/core/styles'; -import { Connect, connect, IDispatcher } from '../../../../framework/src/flux/connect'; -import { MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; -import { TreeView, TreeItem, TreeViewCtorType } from '../../../../framework/src/components/material-ui/treeView'; +import * as React from 'react'; +import { RouteComponentProps, withRouter } from 'react-router-dom'; -import { InventoryType } from '../models/inventory'; +import connect, { IDispatcher, Connect } from "../../../../framework/src/flux/connect"; import { IApplicationStoreState } from "../../../../framework/src/store/applicationStore"; -import { createInventoryElementsProperties, createInventoryElementsActions } from "../handlers/inventoryElementsHandler"; +import { MaterialTable, MaterialTableCtorType, ColumnType } from "../../../../framework/src/components/material-table"; +import { AppBar, Tabs, Tab, MenuItem, Typography } from "@material-ui/core"; +import { PanelId } from "../models/panelId"; +import { setPanelAction } from "../actions/panelActions"; -const styles = (theme: Theme) => createStyles({ - root: { - flex: "1 0 0%", - display: "flex", - flexDirection: "row", - }, - tree: { - flex: "1 0 0%", - minWidth: "250px", - padding: `0px ${theme.spacing(1)}px` - }, - details: { - flex: "5 0 0%", - padding: `0px ${theme.spacing(1)}px` - } -}); -const InventoryTable = MaterialTable as MaterialTableCtorType<InventoryType & {_id: string}>; +import { createConnectedNetworkElementsProperties, createConnectedNetworkElementsActions } from "../handlers/connectedNetworkElementsHandler"; + +import { NetworkElementConnection } from "../models/networkElementConnection"; + +import { InventoryType } from '../models/inventory'; + +import { createInventoryElementsProperties, createInventoryElementsActions } from "../handlers/inventoryElementsHandler"; +import { NavigateToApplication } from '../../../../framework/src/actions/navigationActions'; +import { updateInventoryTreeAsyncAction } from '../actions/inventoryTreeActions'; + +const InventoryTable = MaterialTable as MaterialTableCtorType<InventoryType & { _id: string }>; const mapProps = (state: IApplicationStoreState) => ({ + connectedNetworkElementsProperties: createConnectedNetworkElementsProperties(state), + panelId: state.inventory.currentOpenPanel, inventoryElementsProperties: createInventoryElementsProperties(state), inventoryElements: state.inventory.inventoryElements }); const mapDispatch = (dispatcher: IDispatcher) => ({ - inventoryElementsActions: createInventoryElementsActions(dispatcher.dispatch) + connectedNetworkElementsActions: createConnectedNetworkElementsActions(dispatcher.dispatch), + switchActivePanel: (panelId: PanelId) => { + dispatcher.dispatch(setPanelAction(panelId)); + }, + inventoryElementsActions: createInventoryElementsActions(dispatcher.dispatch), + navigateToApplication: (applicationName: string, path?: string) => dispatcher.dispatch(new NavigateToApplication(applicationName, path)), + updateInventoryTree: (mountId: string, seatchTerm?: string) => dispatcher.dispatch(updateInventoryTreeAsyncAction(mountId, seatchTerm)), }); -const SampleTree = TreeView as any as TreeViewCtorType<string>; +let treeViewInitialSorted = false; +let inventoryInitialSorted = false; +const ConnectedElementTable = MaterialTable as MaterialTableCtorType<NetworkElementConnection>; -type TreeDemoItem = TreeItem<string>; +type DashboardComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDispatch>; -const treeData: TreeDemoItem[] = [ - { - content: "Erste Ebene", children: [ - { - content: "Zweite Ebene", children: [ - { content: "Dritte Ebene" }, - ] - }, - { content: "Zweite Ebene 2" }, - ] - }, - { content: "Erste Ebene 3" }, -]; +class DashboardSelectorComponent extends React.Component<DashboardComponentProps> { + + private onHandleTabChange = (event: React.ChangeEvent<{}>, newValue: PanelId) => { + this.onTogglePanel(newValue); + } + + private onTogglePanel = (panelId: PanelId) => { + const nextActivePanel = panelId; + this.props.switchActivePanel(nextActivePanel); + + switch (nextActivePanel) { + case 'InventoryElementsTable': + + if (!inventoryInitialSorted) { + this.props.inventoryElementsActions.onHandleExplicitRequestSort("nodeId", "asc"); + inventoryInitialSorted = true; + } else { + this.props.inventoryElementsActions.onRefresh(); + + } + break; + case 'TreeviewTable': + if (!treeViewInitialSorted) { + this.props.connectedNetworkElementsActions.onHandleExplicitRequestSort("nodeId", "asc"); + treeViewInitialSorted = true; + } else { + this.props.connectedNetworkElementsActions.onRefresh(); + } + break; + case null: + // do nothing if all panels are closed + break; + default: + console.warn("Unknown nextActivePanel [" + nextActivePanel + "] in connectView"); + break; + } + + }; + + getContextMenu = (rowData: InventoryType) => { + return [ + <MenuItem aria-label={"inventory-button"} onClick={event => { this.props.updateInventoryTree(rowData.nodeId, rowData.uuid); this.props.navigateToApplication("inventory", rowData.nodeId) }}><Typography>View in Treeview</Typography></MenuItem>, + ]; + + } -class DashboardComponent extends React.Component<& WithStyles<typeof styles> & Connect<typeof mapProps, typeof mapDispatch>> { render() { - return <InventoryTable stickyHeader title="Inventory" idProperty="_id" columns={[ - { property: "nodeId", title: "Node Name" }, - { property: "manufacturerIdentifier", title: "Manufacturer" }, - { property: "parentUuid", title: "Parent" }, - { property: "uuid", title: "Name" }, - { property: "serial", title: "Serial" }, - { property: "version", title: "Version" }, - { property: "date", title: "Date" }, - { property: "description", title: "Description" }, - { property: "partTypeId", title: "Part Type Id" }, - { property: "modelIdentifier", title: "Model Identifier" }, - { property: "typeName", title: "Type" }, - { property: "treeLevel", title: "Containment Level" }, - ]} {...this.props.inventoryElementsActions} {...this.props.inventoryElementsProperties} > - </InventoryTable> + + const { panelId: activePanelId } = this.props; + return ( + <> + <AppBar position="static"> + <Tabs value={activePanelId} onChange={this.onHandleTabChange} aria-label="simple tabs example"> + <Tab label="Table View" value="InventoryElementsTable" /> + <Tab label="Tree view" value="TreeviewTable" /> + </Tabs> + </AppBar> + + { + + activePanelId === "InventoryElementsTable" && + + <InventoryTable stickyHeader title="Inventory" idProperty="_id" columns={[ + { property: "nodeId", title: "Node Name" }, + { property: "manufacturerIdentifier", title: "Manufacturer" }, + { property: "parentUuid", title: "Parent" }, + { property: "uuid", title: "Name" }, + { property: "serial", title: "Serial" }, + { property: "version", title: "Version" }, + { property: "date", title: "Date" }, + { property: "description", title: "Description" }, + { property: "partTypeId", title: "Part Type Id" }, + { property: "modelIdentifier", title: "Model Identifier" }, + { property: "typeName", title: "Type" }, + { property: "treeLevel", title: "Containment Level" }, + ]} {...this.props.inventoryElementsActions} {...this.props.inventoryElementsProperties} + createContextMenu={rowData => { + + return this.getContextMenu(rowData); + }} > + </InventoryTable> + + } + { + activePanelId === "TreeviewTable" && + + <ConnectedElementTable stickyHeader onHandleClick={(e, row) => { this.props.history.push(`${this.props.match.path}/${row.nodeId}`) }} columns={[ + { property: "nodeId", title: "Name", type: ColumnType.text }, + { property: "isRequired", title: "Required ?", type: ColumnType.boolean }, + { property: "host", title: "Host", type: ColumnType.text }, + { property: "port", title: "Port", type: ColumnType.numeric }, + { property: "coreModelCapability", title: "Core Model", type: ColumnType.text }, + { property: "deviceType", title: "Type", type: ColumnType.text }, + ]} idProperty="id" {...this.props.connectedNetworkElementsActions} {...this.props.connectedNetworkElementsProperties} asynchronus > + </ConnectedElementTable> + } + </> + ); } componentDidMount() { - this.props.inventoryElementsActions.onToggleFilter(); - this.props.inventoryElementsActions.onHandleRequestSort("node-id"); + + if (this.props.panelId === null) { //set default tab if none is set + this.onTogglePanel("InventoryElementsTable"); + } + } } -export const Dashboard = connect(mapProps, mapDispatch)(withStyles(styles)(DashboardComponent)); -export default Dashboard;
\ No newline at end of file +export const Dashboard = withRouter(connect(mapProps, mapDispatch)(DashboardSelectorComponent)); +export default Dashboard; + diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/views/treeview.tsx b/sdnr/wt/odlux/apps/inventoryApp/src/views/treeview.tsx new file mode 100644 index 000000000..5f2c61080 --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/src/views/treeview.tsx @@ -0,0 +1,132 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ +import * as React from "react"; +import { withStyles, WithStyles, createStyles, Theme } from '@material-ui/core/styles'; + +import { renderObject } from '../../../../framework/src/components/objectDump'; +import { Connect, connect, IDispatcher } from '../../../../framework/src/flux/connect'; +import { TreeView, TreeViewCtorType, SearchMode } from '../../../../framework/src/components/material-ui/treeView'; + +import { IApplicationStoreState } from "../../../../framework/src/store/applicationStore"; + +import { updateInventoryTreeAsyncAction, selectInventoryNodeAsyncAction, UpdateSelectedNodeAction, UpdateExpandedNodesAction, setSearchTermAction} from "../actions/inventoryTreeActions"; +import { TreeDemoItem } from "../models/inventory"; + +import { RouteComponentProps } from "react-router-dom"; + +const styles = (theme: Theme) => createStyles({ + root: { + flex: "1 0 0%", + display: "flex", + flexDirection: "row", + }, + tree: { + flex: "1 0 0%", + minWidth: "250px", + padding: `0px ${theme.spacing(1)}px` + }, + details: { + flex: "5 0 0%", + padding: `0px ${theme.spacing(1)}px` + } +}); + +const mapProps = (state: IApplicationStoreState) => ({ + isBusy: state.inventory.inventoryTree.isBusy, + rootNodes: state.inventory.inventoryTree.rootNodes, + searchTerm: state.inventory.inventoryTree.searchTerm, + selectedNode: state.inventory.inventoryTree.selectedNode, + expendedItems: state.inventory.inventoryTree.expandedItems, +}); + +const mapDispatch = (dispatcher: IDispatcher) => ({ + updateExpendedNodes: (expendedNodes: TreeDemoItem[]) => dispatcher.dispatch(new UpdateExpandedNodesAction(expendedNodes)), + updateInventoryTree: (mountId: string, seatchTerm?: string) => dispatcher.dispatch(updateInventoryTreeAsyncAction(mountId, seatchTerm)), + selectTreeNode: (nodeId?: string) => nodeId ? dispatcher.dispatch(selectInventoryNodeAsyncAction(nodeId)) : dispatcher.dispatch(new UpdateSelectedNodeAction(undefined)), + setSearchTerm: (searchTerm: string) => dispatcher.dispatch(setSearchTermAction(searchTerm)), +}); + +const propsChache = Symbol("PropsCache"); +const InventoryTree = TreeView as any as TreeViewCtorType<string>; + + + +type TreeviewComponentProps = RouteComponentProps<{ mountId: string}> & WithStyles<typeof styles> & Connect<typeof mapProps, typeof mapDispatch> + +type TreeviewComponentState = { + [propsChache]: { + rootNodes?: TreeDemoItem[]; + }; + rootNodes: TreeDemoItem[]; +} + + +class DashboardComponent extends React.Component<TreeviewComponentProps, TreeviewComponentState> { + + constructor (props: TreeviewComponentProps) { + super(props); + + this.state = { + [propsChache]: {}, + rootNodes: [], + }; + } + + static getDerivedStateFromProps(props: TreeviewComponentProps, state: TreeviewComponentState) { + if (state[propsChache].rootNodes != props.rootNodes) { + state = { ...state, rootNodes: props.rootNodes} + } + return state; + } + + render() { + const { classes, updateInventoryTree, updateExpendedNodes, expendedItems, selectedNode, selectTreeNode, searchTerm, match: { params: { mountId }} } = this.props; + return ( + <div className={classes.root}> + <InventoryTree className={classes.tree} items={this.state.rootNodes} enableSearchBar initialSearchTerm={searchTerm} searchMode={SearchMode.OnEnter} searchTerm={searchTerm} + onSearch={(searchTerm) => updateInventoryTree(mountId, searchTerm)} expandedItems={expendedItems} onFolderClick={(item) => { + const indexOfItemToToggle = expendedItems.indexOf(item); + if (indexOfItemToToggle === -1) { + updateExpendedNodes([...expendedItems, item]); + } else { + updateExpendedNodes([ + ...expendedItems.slice(0, indexOfItemToToggle), + ...expendedItems.slice(indexOfItemToToggle + 1), + ]); + } + }} + onItemClick={(elm) => selectTreeNode(elm.value)} /> + <div className={classes.details}>{ + selectedNode && renderObject(selectedNode) || null + }</div> + </div> + ); + } + + componentDidMount() { + const { updateInventoryTree, searchTerm, match: { params: { mountId } }} = this.props; + updateInventoryTree(mountId, searchTerm); + } + + componentWillUnmount(){ + this.props.setSearchTerm(""); + } +} + +export const InventoryTreeView = connect(mapProps, mapDispatch)(withStyles(styles)(DashboardComponent)); +export default InventoryTreeView;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/inventoryApp/webpack.config.js b/sdnr/wt/odlux/apps/inventoryApp/webpack.config.js index 889bb4d8e..d81797c1e 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/webpack.config.js +++ b/sdnr/wt/odlux/apps/inventoryApp/webpack.config.js @@ -146,6 +146,10 @@ module.exports = (env) => { target: "http://localhost:48181", secure: false }, + "/tree/": { + target: "http://localhost:48181", + secure: false + }, "/websocket": { target: "http://localhost:48181", ws: true, |