Add dashboard to odlux 58/122458/1
authorAijana Schumann <aijana.schumann@highstreet-technologies.com>
Tue, 6 Jul 2021 14:01:10 +0000 (16:01 +0200)
committerAijana Schumann <aijana.schumann@highstreet-technologies.com>
Tue, 6 Jul 2021 14:01:10 +0000 (16:01 +0200)
Add connected element and fault info to home page

Issue-ID: CCSDK-3238
Signed-off-by: Aijana Schumann <aijana.schumann@highstreet-technologies.com>
Change-Id: Ia47442fd0877b721d25d9f97e3a19088df193439

27 files changed:
sdnr/wt/odlux/apps/connectApp/src/actions/commonNetworkElementsActions.ts
sdnr/wt/odlux/apps/connectApp/src/actions/connectionStatusCountActions.ts [new file with mode: 0644]
sdnr/wt/odlux/apps/connectApp/src/handlers/connectAppRootHandler.ts
sdnr/wt/odlux/apps/connectApp/src/handlers/connectionStatusCountHandler.ts [new file with mode: 0644]
sdnr/wt/odlux/apps/connectApp/src/models/connectionStatusCount.ts [new file with mode: 0644]
sdnr/wt/odlux/apps/connectApp/src/pluginConnect.tsx
sdnr/wt/odlux/apps/connectApp/src/services/connectionStatusCountService.ts [new file with mode: 0644]
sdnr/wt/odlux/apps/faultApp/src/pluginFault.tsx
sdnr/wt/odlux/apps/faultApp/src/views/faultApplication.tsx
sdnr/wt/odlux/framework/package.json
sdnr/wt/odlux/framework/pom.xml
sdnr/wt/odlux/framework/src/app.css
sdnr/wt/odlux/framework/src/app.tsx
sdnr/wt/odlux/framework/src/assets/images/odluxLogo.gif [new file with mode: 0644]
sdnr/wt/odlux/framework/src/assets/images/odluxLogo.gif.d.ts [new file with mode: 0644]
sdnr/wt/odlux/framework/src/assets/images/onapLogo.gif [new file with mode: 0644]
sdnr/wt/odlux/framework/src/assets/images/onapLogo.gif.d.ts [new file with mode: 0644]
sdnr/wt/odlux/framework/src/components/errorDisplay.tsx
sdnr/wt/odlux/framework/src/design/default.ts
sdnr/wt/odlux/framework/src/handlers/applicationStateHandler.ts
sdnr/wt/odlux/framework/src/index.dev.html
sdnr/wt/odlux/framework/src/services/notificationService.ts
sdnr/wt/odlux/framework/src/views/about.tsx
sdnr/wt/odlux/framework/src/views/home.tsx
sdnr/wt/odlux/odlux.properties
sdnr/wt/odlux/package.json
sdnr/wt/odlux/yarn.lock

index a83e002..0c32662 100644 (file)
@@ -28,7 +28,7 @@ import { connectionStatusLogReloadAction } from '../handlers/connectionStatusLog
 
 import { PanelId } from '../models/panelId';
 import { guiCutThrough } from '../models/guiCutTrough';
-import { connectService}  from '../services/connectService';
+import { connectService} from '../services/connectService';
 
 
 export class SetPanelAction extends Action {
diff --git a/sdnr/wt/odlux/apps/connectApp/src/actions/connectionStatusCountActions.ts b/sdnr/wt/odlux/apps/connectApp/src/actions/connectionStatusCountActions.ts
new file mode 100644 (file)
index 0000000..e1e16b7
--- /dev/null
@@ -0,0 +1,52 @@
+/**
+ * ============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 { getConnectionStatusCountStateFromDatabase } from '../services/connectionStatusCountService';
+import { Dispatch } from '../../../../framework/src/flux/store';
+
+import { Action } from '../../../../framework/src/flux/action';
+
+
+export class ConnectionStatusCountBaseAction extends Action { }
+
+
+export class SetConnectionStatusCountAction extends ConnectionStatusCountBaseAction {
+  constructor(public ConnectedCount: number, public ConnectingCount: number, public DisconnectedCount: number,
+    public MountedCount: number, public UnableToConnectCount: number, public UndefinedCount: number, public UnmountedCount: number, public totalCount: number) {
+    super();
+  }
+}
+
+
+export const refreshConnectionStatusCountAsyncAction = async (dispatch: Dispatch) => {
+  const result = await getConnectionStatusCountStateFromDatabase().catch(_ => null);
+  if (result) {
+    const statusAction = new SetConnectionStatusCountAction(
+      result["Connected"] || 0,
+      result["Connecting"] || 0,
+      result["Disconnected"] || 0,
+      result["Mounted"] || 0,
+      result["UnableToConnect"] || 0,
+      result["Undefined"] || 0,
+      result["Unmounted"] || 0,
+      result["total"] || 0
+    );
+    dispatch(statusAction);
+    return;
+  }
+  dispatch(new SetConnectionStatusCountAction(0, 0, 0, 0, 0, 0, 0, 0));
+}
index c23e439..70b64c9 100644 (file)
@@ -24,6 +24,7 @@ import { IInfoNetworkElementsState, infoNetworkElementsActionHandler } from './i
 import { SetPanelAction, AddWebUriList, RemoveWebUri, SetWeburiSearchBusy } from '../actions/commonNetworkElementsActions';
 import { PanelId } from '../models/panelId';
 import { guiCutThrough } from '../models/guiCutTrough';
+import { connectionStatusCountHandler, IConnectionStatusCount } from './connectionStatusCountHandler';
 
 export interface IConnectAppStoreState {
   networkElements: INetworkElementsState;
@@ -31,6 +32,7 @@ export interface IConnectAppStoreState {
   currentOpenPanel: PanelId;
   elementInfo: IInfoNetworkElementsState;
   guiCutThrough: guiCutThroughState;
+  connectionStatusCount: IConnectionStatusCount;
 }
 
 const currentOpenPanelHandler: IActionHandler<PanelId> = (state = null, action) => {
@@ -87,7 +89,8 @@ const actionHandlers = {
   connectionStatusLog: connectionStatusLogActionHandler,
   currentOpenPanel: currentOpenPanelHandler,
   elementInfo: infoNetworkElementsActionHandler,
-  guiCutThrough: guiCutThroughHandler
+  guiCutThrough: guiCutThroughHandler,
+  connectionStatusCount: connectionStatusCountHandler
 };
 
 export const connectAppRootHandler = combineActionHandler<IConnectAppStoreState>(actionHandlers);
diff --git a/sdnr/wt/odlux/apps/connectApp/src/handlers/connectionStatusCountHandler.ts b/sdnr/wt/odlux/apps/connectApp/src/handlers/connectionStatusCountHandler.ts
new file mode 100644 (file)
index 0000000..6117865
--- /dev/null
@@ -0,0 +1,58 @@
+/**
+ * ============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 { SetConnectionStatusCountAction } from "../actions/connectionStatusCountActions";
+
+export interface IConnectionStatusCount {
+   Connected: number,
+   Connecting: number,
+   Disconnected: number,
+   Mounted: number,
+   UnableToConnect: number,
+   Undefined: number,
+   Unmounted: number,
+   total: number
+}
+
+const connectionStatusCountInit: IConnectionStatusCount = {
+   Connected: 0,
+   Connecting: 0,
+   Disconnected: 0,
+   Mounted: 0,
+   UnableToConnect: 0,
+   Undefined: 0,
+   Unmounted: 0,
+   total: 0
+};
+
+export const connectionStatusCountHandler: IActionHandler<IConnectionStatusCount> = (state = connectionStatusCountInit, action) => {
+   if (action instanceof SetConnectionStatusCountAction) {
+      state = {
+         Connected: action.ConnectedCount,
+         Connecting: action.ConnectingCount,
+         Disconnected: action.DisconnectedCount,
+         Mounted: action.MountedCount,
+         UnableToConnect: action.UnableToConnectCount,
+         Undefined: action.UndefinedCount,
+         Unmounted: action.UnmountedCount,
+         total: action.totalCount
+      }
+   }
+
+   return state;
+}
\ No newline at end of file
diff --git a/sdnr/wt/odlux/apps/connectApp/src/models/connectionStatusCount.ts b/sdnr/wt/odlux/apps/connectApp/src/models/connectionStatusCount.ts
new file mode 100644 (file)
index 0000000..125a6e3
--- /dev/null
@@ -0,0 +1,43 @@
+/**
+ * ============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.number (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.number
+ *
+ * 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 ConnectionStatusCountReturnType = {
+  Connected: number,
+  Connecting: number,
+  Disconnected: number,
+  Mounted: number,
+  UnableToConnect: number,
+  Undefined: number,
+  Unmounted: number,
+  total: number
+};
+
+export type ConnectionStatusCountType = {
+  Connected: number,
+  Connecting: number,
+  Disconnected: number,
+  Mounted: number,
+  UnableToConnect: number,
+  Undefined: number,
+  Unmounted: number,
+  total: number
+};
+
+export type ConnectionStatusCount = {
+  'network-element-connections': ConnectionStatusCountReturnType
+};
index 93bed1a..461e140 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
+import * as React from "react";
+import { withRouter, RouteComponentProps, Route, Switch, Redirect } from 'react-router-dom';
 import { faPlug } from '@fortawesome/free-solid-svg-icons';
 
 import applicationManager from '../../../framework/src/services/applicationManager';
 import { subscribe, IFormatedMessage } from '../../../framework/src/services/notificationService';
 import { AddSnackbarNotification } from '../../../framework/src/actions/snackbarActions';
+import { IApplicationStoreState } from "../../../framework/src/store/applicationStore";
+import connect, { Connect, IDispatcher } from '../../../framework/src/flux/connect';
 
+import { findWebUrisForGuiCutThroughAsyncAction, updateCurrentViewAsyncAction, SetPanelAction } from './actions/commonNetworkElementsActions';
+import { refreshConnectionStatusCountAsyncAction } from './actions/connectionStatusCountActions';
+import { createNetworkElementsActions, createNetworkElementsProperties, networkElementsReloadAction } from './handlers/networkElementsHandler';
 import connectAppRootHandler from './handlers/connectAppRootHandler';
 import ConnectApplication from './views/connectView';
+import { PanelId } from "./models/panelId";
+import { NetworkElementsList } from './components/networkElements'
 
-import { findWebUrisForGuiCutThroughAsyncAction, updateCurrentViewAsyncAction } from './actions/commonNetworkElementsActions';
+let currentStatus: string | undefined = undefined;
+
+const mapProps = (state: IApplicationStoreState) => ({
+  currentProblemsProperties: createNetworkElementsProperties(state),
+});
+
+const mapDisp = (dispatcher: IDispatcher) => ({
+  currentProblemsActions: createNetworkElementsActions(dispatcher.dispatch, true),
+  setCurrentPanel: (panelId: PanelId) => dispatcher.dispatch(new SetPanelAction(panelId)),
+});
+
+const ConnectApplicationRouteAdapter = connect(mapProps, mapDisp)((props: RouteComponentProps<{ status?: string }> & Connect<typeof mapProps, typeof mapDisp>) => {
+  if (currentStatus !== props.match.params.status) {
+    currentStatus = props.match.params.status || undefined;
+    window.setTimeout(() => {
+      if (currentStatus) {
+        props.setCurrentPanel("NetworkElements");
+        props.currentProblemsActions.onFilterChanged("status", currentStatus);
+        if (!props.currentProblemsProperties.showFilter) {
+          props.currentProblemsActions.onToggleFilter(false);
+          props.currentProblemsActions.onRefresh();
+        }
+        else
+          props.currentProblemsActions.onRefresh();
+      }
+    });
+  }
+  return (
+    <NetworkElementsList />
+  )
+});
+
+
+const App = withRouter((props: RouteComponentProps) => (
+  <Switch>
+    <Route path={`${props.match.path}/connectionStatus/:status?`} component={ConnectApplicationRouteAdapter} />
+    <Route path={`${props.match.path}`} component={ConnectApplication} />
+    <Redirect to={`${props.match.path}`} />
+  </Switch>
+));
 
 export function register() {
   const applicationApi = applicationManager.registerApplication({
     name: "connect",
     icon: faPlug,
-    rootComponent: ConnectApplication,
+    rootComponent: App,
     rootActionHandler: connectAppRootHandler,
     menuEntry: "Connect"
   });
@@ -52,4 +100,17 @@ export function register() {
       });
     }
   }));
+
+  applicationApi.applicationStoreInitialized.then(store => {
+    store.dispatch(networkElementsReloadAction);
+  });
+
+  applicationApi.applicationStoreInitialized.then(store => {
+    store.dispatch(refreshConnectionStatusCountAsyncAction);
+  });
+  window.setInterval(() => {
+    applicationApi.applicationStoreInitialized.then(store => {
+      store.dispatch(refreshConnectionStatusCountAsyncAction);
+    });
+  }, 15000);
 }
\ No newline at end of file
diff --git a/sdnr/wt/odlux/apps/connectApp/src/services/connectionStatusCountService.ts b/sdnr/wt/odlux/apps/connectApp/src/services/connectionStatusCountService.ts
new file mode 100644 (file)
index 0000000..519c965
--- /dev/null
@@ -0,0 +1,54 @@
+/**
+ * ============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 { requestRest } from "../../../../framework/src/services/restService";
+import { Result } from "../../../../framework/src/models/elasticSearch";
+import { ConnectionStatusCountType, ConnectionStatusCount } from "../models/connectionStatusCount";
+
+
+
+export const getConnectionStatusCountStateFromDatabase = async (): Promise<ConnectionStatusCountType | null> => {
+  const path = 'rests/operations/data-provider:read-status';
+  const result = await requestRest<Result<ConnectionStatusCount>>(path, { method: "POST" });
+  let connectionStatusCountType: ConnectionStatusCountType = {
+    Connected: 0,
+    Connecting: 0,
+    Disconnected: 0,
+    Mounted: 0,
+    UnableToConnect: 0,
+    Undefined: 0,
+    Unmounted: 0,
+    total: 0
+  }
+  let connectionStatusCount: ConnectionStatusCount[] | null = null;
+
+  if (result && result["data-provider:output"] && result["data-provider:output"].data) {
+    connectionStatusCount = result["data-provider:output"].data;
+    connectionStatusCountType = {
+      Connected: connectionStatusCount[0]["network-element-connections"].Connected,
+      Connecting: connectionStatusCount[0]["network-element-connections"].Connecting,
+      Disconnected: connectionStatusCount[0]["network-element-connections"].Disconnected,
+      Mounted: connectionStatusCount[0]["network-element-connections"].Mounted,
+      UnableToConnect: connectionStatusCount[0]["network-element-connections"].UnableToConnect,
+      Undefined: connectionStatusCount[0]["network-element-connections"].Undefined,
+      Unmounted: connectionStatusCount[0]["network-element-connections"].Unmounted,
+      total: connectionStatusCount[0]["network-element-connections"].total,
+    }
+  }
+  return connectionStatusCountType;
+}
index bf96fe3..0629941 100644 (file)
@@ -42,6 +42,7 @@ import { FaultStatus } from "./components/faultStatus";
 import { refreshFaultStatusAsyncAction } from "./actions/statusActions";
 
 let currentMountId: string | undefined = undefined;
+let currentSeverity: string | undefined = undefined;
 
 const mapProps = (state: IApplicationStoreState) => ({
   currentProblemsProperties: createCurrentProblemsProperties(state),
@@ -75,8 +76,30 @@ const FaultApplicationRouteAdapter = connect(mapProps, mapDisp)((props: RouteCom
   )
 });
 
+const FaulttApplicationAlarmStatusRouteAdapter = connect(mapProps, mapDisp)((props: RouteComponentProps<{ severity?: string }> & Connect<typeof mapProps, typeof mapDisp>) => {
+  if (currentSeverity !== props.match.params.severity) {
+    currentSeverity = props.match.params.severity || undefined;
+    window.setTimeout(() => {
+      if (currentSeverity) {
+        props.setCurrentPanel("CurrentProblem");
+        props.currentProblemsActions.onFilterChanged("severity", currentSeverity);
+        if (!props.currentProblemsProperties.showFilter) {
+          props.currentProblemsActions.onToggleFilter(false);
+          props.currentProblemsActions.onRefresh();
+        }
+        else
+          props.currentProblemsActions.onRefresh();
+      }
+    });
+  }
+  return (
+    <FaultApplication />
+  )
+});
+
 const App = withRouter((props: RouteComponentProps) => (
   <Switch>
+    <Route path={`${props.match.path}/alarmStatus/:severity?`} component={FaulttApplicationAlarmStatusRouteAdapter} />
     <Route path={`${props.match.path}/:mountId?`} component={FaultApplicationRouteAdapter} />
     <Redirect to={`${props.match.path}`} />
   </Switch>
index 6075066..7b0c236 100644 (file)
@@ -137,7 +137,7 @@ class FaultApplicationComponent extends React.Component<FaultApplicationComponen
 
   render(): JSX.Element {
 
-    const refreshButton = {
+    const clearAlarmsAction = {
       icon: Sync, tooltip: 'Clear stuck alarms', onClick: this.onDialogOpen
     };
 
@@ -158,7 +158,7 @@ class FaultApplicationComponent extends React.Component<FaultApplicationComponen
     };
 
     const areFaultsAvailable = this.props.currentProblemsProperties.rows && this.props.currentProblemsProperties.rows.length > 0
-    const customActions = areFaultsAvailable ? [refreshButton, refreshCurrentProblemsAction] : [refreshCurrentProblemsAction];
+    const customActions = areFaultsAvailable ? [clearAlarmsAction, refreshCurrentProblemsAction] : [refreshCurrentProblemsAction];
 
     const { panelId: activePanelId } = this.props;
 
@@ -191,7 +191,7 @@ class FaultApplicationComponent extends React.Component<FaultApplicationComponen
         }
         {activePanelId === 'AlarmNotifications' &&
 
-          <FaultAlarmNotificationTable tableId="alarm-notifications-table" idProperty="id" stickyHeader rows={this.props.faultNotifications.faults} asynchronus columns={[
+          <FaultAlarmNotificationTable stickyHeader tableId="alarm-notifications-table" idProperty="id" defaultSortColumn='timeStamp' defaultSortOrder='desc' rows={this.props.faultNotifications.faults} asynchronus columns={[
             { property: "icon", title: "", type: ColumnType.custom, customControl: this.renderIcon },
             { property: "timeStamp", title: "Timestamp" },
             { property: "nodeName", title: "Node Name" },
index 345fc8b..c2ad49c 100644 (file)
@@ -37,8 +37,8 @@
     "react": "17.0.1",\r
     "react-dom": "17.0.1",\r
     "react-router-dom": "5.2.0",\r
-    "@fortawesome/react-fontawesome": "0.1.3",\r
-    "@fortawesome/fontawesome-svg-core": "1.2.12",\r
+    "@fortawesome/react-fontawesome": "0.1.14",\r
+    "@fortawesome/fontawesome-svg-core": "1.2.35",\r
     "@fortawesome/free-solid-svg-icons": "5.6.3",\r
     "jsonwebtoken": "8.3.0",\r
     "@types/jsonwebtoken": "7.2.8"\r
@@ -46,6 +46,8 @@
   "dependencies": {\r
     "@babel/polyfill": "^7.0.0",\r
     "@types/x2js": "^3.1.0",\r
-    "http-server": "^0.11.1"\r
+    "chart.js": "^3.4.0",\r
+    "http-server": "^0.11.1",\r
+    "react-chartjs-2": "^3.0.3"\r
   }\r
 }\r
index 9d648b5..3e94753 100644 (file)
@@ -46,7 +46,7 @@
     <properties>
         <buildtime>${maven.build.timestamp}</buildtime>
         <distversion>ONAP Frankfurt (Neon, mdsal ${odl.mdsal.version})</distversion>
-        <buildno>96.078ad12(21/03/25)</buildno>
+        <buildno>110.0d5d064(21/07/05)</buildno>
         <odlux.version>ONAP SDN-R | ONF Wireless for ${distversion} - Build: ${buildtime} ${buildno} ${project.version}</odlux.version>
     </properties>
 
index f70fbc0..9b653b3 100644 (file)
@@ -1,5 +1,6 @@
 html, body, #app {
   height: 100%;
+  min-width: 1000px;
   padding: 0px;
   margin: 0px;
   font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
index 2d913be..7e162cd 100644 (file)
@@ -106,13 +106,3 @@ export const runApplication = () => {
 \r
 };\r
 \r
-if (process.env.NODE_ENV === "development") {\r
-  const addTransportPCEUrl = () =>{\r
-    const url = window.localStorage.getItem(transportPCEUrl);\r
-    if(url === null){\r
-        window.localStorage.setItem(transportPCEUrl, "http://10.20.6.32:18082/");\r
-        console.log("set transport url :D")\r
-    }\r
-  }\r
-  addTransportPCEUrl();\r
-}\r
diff --git a/sdnr/wt/odlux/framework/src/assets/images/odluxLogo.gif b/sdnr/wt/odlux/framework/src/assets/images/odluxLogo.gif
new file mode 100644 (file)
index 0000000..ad188a8
Binary files /dev/null and b/sdnr/wt/odlux/framework/src/assets/images/odluxLogo.gif differ
diff --git a/sdnr/wt/odlux/framework/src/assets/images/odluxLogo.gif.d.ts b/sdnr/wt/odlux/framework/src/assets/images/odluxLogo.gif.d.ts
new file mode 100644 (file)
index 0000000..3613183
--- /dev/null
@@ -0,0 +1,20 @@
+/**
+ * ============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==========================================================================
+ */
+
+declare const odluxLogo: string;
+export default odluxLogo;
\ No newline at end of file
diff --git a/sdnr/wt/odlux/framework/src/assets/images/onapLogo.gif b/sdnr/wt/odlux/framework/src/assets/images/onapLogo.gif
new file mode 100644 (file)
index 0000000..cd7eb8f
Binary files /dev/null and b/sdnr/wt/odlux/framework/src/assets/images/onapLogo.gif differ
diff --git a/sdnr/wt/odlux/framework/src/assets/images/onapLogo.gif.d.ts b/sdnr/wt/odlux/framework/src/assets/images/onapLogo.gif.d.ts
new file mode 100644 (file)
index 0000000..5bfb37a
--- /dev/null
@@ -0,0 +1,20 @@
+/**
+ * ============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==========================================================================
+ */
+
+ declare const onapLogo: string;
+ export default onapLogo;
\ No newline at end of file
index 4cf63e9..1060d4b 100644 (file)
@@ -72,7 +72,7 @@ type ErrorDisplayProps = WithStyles<typeof styles> & Connect;
 // }
 
 /**
- * Represents a compnent for formaing and displaying errors.
+ * Represents a component for formatting and displaying errors.
  */
 class ErrorDisplayComponent extends React.Component<ErrorDisplayProps> {
   constructor(props: ErrorDisplayProps) {
index 542c436..59b8c20 100644 (file)
  *****************************************************************************/
 
 import { createMuiTheme } from '@material-ui/core/styles';
+import onapLogo from '../assets/images/onapLogo.gif'
 
 const theme = createMuiTheme({
   design: {
     id: "onap",
     name: "Open Networking Automation Plattform (ONAP)",
-    url: "https://www.onap.org/wp-content/uploads/sites/20/2017/02/logo_onap_2017.png",
+    url: onapLogo,
     height: 49,
     width: 229,
     logoHeight: 32,
index 06df670..6426066 100644 (file)
@@ -29,7 +29,16 @@ import { ErrorInfo } from '../models/errorInfo';
 import { SnackbarItem } from '../models/snackbarItem';
 import { ExternalLoginProvider } from '../models/externalLoginProvider';
 import { ApplicationConfig } from '../models/applicationConfig';
+import { IConnectAppStoreState } from '../../../apps/connectApp/src/handlers/connectAppRootHandler';
+import { IFaultAppStoreState } from '../../../apps/faultApp/src/handlers/faultAppRootHandler';
 
+
+declare module '../store/applicationStore' {
+  interface IApplicationStoreState {
+    connect: IConnectAppStoreState;
+    fault: IFaultAppStoreState
+  }
+}
 export interface IApplicationState {
   title: string;
   appId?: string;
@@ -44,12 +53,12 @@ export interface IApplicationState {
   enablePolicy: boolean             // false 
 }
 
-const applicationStateInit: IApplicationState = { 
-  title: "Loading ...", 
-  errors: [], 
-  snackBars: [], 
-  isMenuOpen: true, 
-  isMenuClosedByUser: false, 
+const applicationStateInit: IApplicationState = {
+  title: "Loading ...",
+  errors: [],
+  snackBars: [],
+  isMenuOpen: true,
+  isMenuClosedByUser: false,
   isWebsocketAvailable: undefined,
   externalLoginProviders: null,
   authentication: "basic",
index 240da26..6c95638 100644 (file)
   </script>
   <script>
     // run the application
-    require(["app" /*,"connectApp","inventoryApp","faultApp","helpApp"*/], function (app,connectApp,inventoryApp,faultApp,helpApp) {
-      // connectApp.register();
+    require(["app" ,"connectApp","faultApp"/*,"inventoryApp","helpApp"*/], function (app,connectApp,faultApp,/*inventoryApp,helpApp*/) {
+      connectApp.register();
+      faultApp.register();
       // inventoryApp.register();
-      // faultApp.register();
       // helpApp.register();
       app("./app.tsx").runApplication();
     });
index 5625b1f..99e697e 100644 (file)
@@ -51,7 +51,7 @@ function setCurrentSubscriptions(notificationSocket: WebSocket) {
       'data': 'scopes',
       'scopes':[{
         "schema":{
-            "namespace":"*",
+            "namespace":"urn:opendaylight:params:xml:ns:yang:devicemanager",
             "revision":"*",
             "notification": scopesToSubscribe 
          }
index 5d2257a..174444f 100644 (file)
@@ -97,7 +97,14 @@ class AboutComponent extends React.Component<any, AboutState> {
 
   private loadAboutContent(): void {
     const baseUri = window.location.pathname.substring(0,window.location.pathname.lastIndexOf("/")+1);
-    const p1 = requestRestExt<string>('/about');
+    const init = {
+      'method': 'GET',
+      headers: {
+        'Content-Type': 'application/json',
+        'Accept': 'text/markdown',
+      }
+    };
+    const p1 = requestRestExt<string>('/about',init);
     const p2 = requestRestExt<odluxVersion>(`${baseUri}version.json`);
     const p3 = requestRestExt<any>(`/topology/info/version`);
 
index 4a4084e..b5df052 100644 (file)
  */
 import * as React from 'react';
 
-export const Home = (props: React.Props<any>) => {
-  return (
-    <div>
-      <h1>Welcome to ODLUX.</h1>
-    </div>
-  )
+import { withRouter, RouteComponentProps } from 'react-router-dom';
+import connect, { Connect, IDispatcher } from '..//flux/connect';
+import { IApplicationState } from '../handlers/applicationStateHandler';
+import { IApplicationStoreState } from '../store/applicationStore';
+import { WithStyles, withStyles, createStyles, Theme } from '@material-ui/core/styles';
+import { Doughnut } from 'react-chartjs-2';
+import { NavigateToApplication } from '../actions/navigationActions';
+
+const styles = (theme: Theme) => createStyles({
+  pageWidthSettings: {
+    width: '50%',
+    float: 'left'
+  },
+})
+
+const scrollbar = { overflow: "auto", paddingRight: "20px" }
+
+const mapProps = (state: IApplicationStoreState) => ({
+  connectionStatusCount: state.connect.connectionStatusCount,
+  alarmStatus: state.fault.faultStatus
+});
+
+const mapDispatch = (dispatcher: IDispatcher) => ({
+  navigateToApplication: (applicationName: string, path?: string) => dispatcher.dispatch(new NavigateToApplication(applicationName, path)),
+});
+
+type HomeComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDispatch> & WithStyles<typeof styles>;
+
+class Home extends React.Component<HomeComponentProps>  {
+  constructor(props: HomeComponentProps) {
+    super(props);
+    this.state = {
+    }
+  }
+
+  render(): JSX.Element {
+    const { classes } = this.props;
+
+    /** Available Network Connection Status chart data */
+    const connectionStatusData = {
+      labels: ['Connected', 'Connecting', 'Disconnected', 'UnableToConnect'],
+      datasets: [{
+        data: [
+          this.props.connectionStatusCount.Connected,
+          this.props.connectionStatusCount.Connecting,
+          this.props.connectionStatusCount.Disconnected,
+          this.props.connectionStatusCount.UnableToConnect
+        ],
+        backgroundColor: [
+          'rgb(0, 153, 51)',
+          'rgb(255, 102, 0)',
+          'rgb(191, 191, 191)',
+          'rgb(191, 191, 191)'
+        ]
+      }]
+    };
+
+
+    /** No Devices available */
+    const connectionStatusUnavailableData = {
+      labels: ['No Devices available'],
+      datasets: [{
+        data: [1],
+        backgroundColor: [
+          'rgb(255, 255, 255)'
+        ]
+      }]
+    };
+
+    /** Connection status options */
+    let labels: String[] = ['Connected', 'Connecting', 'Disconnected', 'UnableToConnect'];
+    const connectionStatusOptions = {
+      responsive: true,
+      maintainAspectRatio: false,
+      plugins: {
+        legend: {
+          display: true,
+          position: 'top'
+        }
+      },
+      onClick: (event: MouseEvent, item: any) => {
+        if (item[0]) {
+          let connectionStatus = labels[item[0].index] + '';
+          this.props.navigateToApplication("connect", '/connectionStatus/' + connectionStatus);
+        }
+      }
+    }
+
+    /** Connection status unavailable options */
+    const connectionStatusUnavailableOptions = {
+      responsive: true,
+      maintainAspectRatio: false,
+      plugins: {
+        legend: {
+          display: true,
+          position: 'top'
+        },
+        tooltip: {
+          enabled: false
+        }
+      }
+    }
+
+    /** Add text inside the doughnut chart for Connection Status */
+    const connectionStatusPlugins = [{
+      beforeDraw: function (chart: any) {
+        var width = chart.width,
+          height = chart.height,
+          ctx = chart.ctx;
+        ctx.restore();
+        var fontSize = (height / 480).toFixed(2);
+        ctx.font = fontSize + "em sans-serif";
+        ctx.textBaseline = "top";
+        var text = "Network Connection Status",
+          textX = Math.round((width - ctx.measureText(text).width) / 2),
+          textY = height / 2;
+        ctx.fillText(text, textX, textY);
+        ctx.save();
+      }
+    }]
+
+    /** Alarm status Data */
+    const alarmStatusData = {
+      labels: [
+        'Critical',
+        'Major',
+        'Minor',
+        'Warning'
+      ],
+      datasets: [{
+        data: [
+          this.props.alarmStatus.critical,
+          this.props.alarmStatus.major,
+          this.props.alarmStatus.minor,
+          this.props.alarmStatus.warning
+        ],
+        backgroundColor: [
+          'rgb(240, 25, 10)',
+          'rgb(240, 133, 10)',
+          'rgb(240, 240, 10)',
+          'rgb(46, 115, 176)'
+        ],
+      }]
+    }
+
+    /** No Alarm status available */
+    const alarmStatusUnavailableData = {
+      labels: ['No Alarms available'],
+      datasets: [{
+        data: [1],
+        backgroundColor: [
+          'rgb(0, 153, 51)'
+        ]
+      }]
+    };
+
+    /** Alarm status Options */
+    let alarmLabels: String[] = ['Critical', 'Major', 'Minor', 'Warning'];
+    const alarmStatusOptions = {
+      responsive: true,
+      maintainAspectRatio: false,
+      plugins: {
+        legend: {
+          display: true,
+          position: 'top'
+        }
+      },
+      onClick: (event: MouseEvent, item: any) => {
+        if (item[0]) {
+          let severity = alarmLabels[item[0].index] + '';
+          this.props.navigateToApplication("fault", '/alarmStatus/' + severity);
+        }
+      },
+    };
+
+    /** Alarm status unavailable options */
+    const alarmStatusUnavailableOptions = {
+      responsive: true,
+      maintainAspectRatio: false,
+      plugins: {
+        legend: {
+          display: true,
+          position: 'top'
+        },
+        tooltip: {
+          enabled: false
+        }
+      }
+    }
+    /** Add text inside the doughnut chart for Alarm Status */
+    const alarmStatusPlugins = [{
+      beforeDraw: function (chart: any) {
+        var width = chart.width,
+          height = chart.height,
+          ctx = chart.ctx;
+        ctx.restore();
+        var fontSize = (height / 480).toFixed(2);
+        ctx.font = fontSize + "em sans-serif";
+        ctx.textBaseline = "top";
+        var text = "Network Alarm Status",
+          textX = Math.round((width - ctx.measureText(text).width) / 2),
+          textY = height / 2;
+        ctx.fillText(text, textX, textY);
+        ctx.save();
+      }
+    }]
+
+    return (
+      <>
+        <div style={scrollbar} >
+          <h1>Welcome to ODLUX</h1>
+          <div className={classes.pageWidthSettings}>
+            {this.checkConnectionStatus() ?
+              <Doughnut
+                data={connectionStatusData}
+                type={Doughnut}
+                width={500}
+                height={500}
+                options={connectionStatusOptions}
+                plugins={connectionStatusPlugins}
+              />
+              : <Doughnut
+                data={connectionStatusUnavailableData}
+                type={Doughnut}
+                width={500}
+                height={500}
+                options={connectionStatusUnavailableOptions}
+                plugins={connectionStatusPlugins}
+              />
+            }
+          </div>
+          <div className={classes.pageWidthSettings}>
+            {this.checkAlarmStatus() ?
+              <Doughnut
+                data={alarmStatusData}
+                type={Doughnut}
+                width={500}
+                height={500}
+                options={alarmStatusOptions}
+                plugins={alarmStatusPlugins}
+              />
+              : <Doughnut
+                data={alarmStatusUnavailableData}
+                type={Doughnut}
+                width={500}
+                height={500}
+                options={alarmStatusUnavailableOptions}
+                plugins={alarmStatusPlugins}
+              />
+            }
+          </div>
+        </div>
+      </>
+    )
+  }
+
+  /** Check if connection status data available */
+  public checkConnectionStatus = () => {
+    let statusCount = this.props.connectionStatusCount;
+    if (statusCount.Connected == 0 && statusCount.Connecting == 0 && statusCount.Disconnected == 0 && statusCount.UnableToConnect == 0) {
+      return false;
+    }
+    else
+      return true;
+  }
+
+  /** Check if alarms data available */
+  public checkAlarmStatus = () => {
+    let alarmCount = this.props.alarmStatus;
+    if (alarmCount.critical == 0 && alarmCount.major == 0 && alarmCount.minor == 0 && alarmCount.warning == 0) {
+      return false;
+    }
+    else
+      return true;
+  }
+
 }
-export default Home;
\ No newline at end of file
+
+export default withStyles(styles)(withRouter(connect(mapProps, mapDispatch)(Home)));
\ No newline at end of file
index 36de5d4..3b41140 100644 (file)
@@ -1,8 +1,8 @@
-odlux.framework.buildno=96.078ad12(21/03/25)
+odlux.framework.buildno=110.0d5d064(21/07/05)
 odlux.apps.configurationApp.buildno=96.078ad12(21/03/25)
-odlux.apps.connectApp.buildno=96.078ad12(21/03/25)
+odlux.apps.connectApp.buildno=110.0d5d064(21/07/05)
 odlux.apps.eventLogApp.buildno=96.078ad12(21/03/25)
-odlux.apps.faultApp.buildno=96.078ad12(21/03/25)
+odlux.apps.faultApp.buildno=110.0d5d064(21/07/05)
 odlux.apps.helpApp.buildno=96.078ad12(21/03/25)
 odlux.apps.inventoryApp.buildno=96.078ad12(21/03/25)
 odlux.apps.linkCalculationApp.buildno=96.078ad12(21/03/25)
index 5bfcf74..15e607a 100644 (file)
@@ -11,9 +11,9 @@
     "test": "jest --no-cache --coverage --config jest.json"
   },
   "dependencies": {
-    "@fortawesome/fontawesome-svg-core": "1.2.12",
+    "@fortawesome/fontawesome-svg-core": "1.2.35",
     "@fortawesome/free-solid-svg-icons": "5.6.3",
-    "@fortawesome/react-fontawesome": "0.1.3",
+    "@fortawesome/react-fontawesome": "0.1.14",
     "@material-ui/core": "4.11.0",
     "@material-ui/icons": "4.9.1",
     "@material-ui/lab": "4.0.0-alpha.41",
index 842aebb..7648c7e 100644 (file)
   resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.32.tgz#3436795d5684f22742989bfa08f46f50f516f259"
   integrity sha512-ux2EDjKMpcdHBVLi/eWZynnPxs0BtFVXJkgHIxXRl+9ZFaHPvYamAfCzeeQFqHRjuJtX90wVnMRaMQAAlctz3w==
 
-"@fortawesome/fontawesome-svg-core@1.2.12":
-  version "1.2.12"
-  resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.12.tgz#9732617b1e484435f6946645d7f9ad248f12c437"
-  integrity sha512-cqTfa3vZ+Z9UYQnmLfCOwyLnf0Xcoxkmm/BSaI29Yikzu9zIeD4es7lBZMDqLOXYSEQC+rCr8caxFlGJcJVA+w==
+"@fortawesome/fontawesome-common-types@^0.2.35":
+  version "0.2.35"
+  resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.35.tgz#01dd3d054da07a00b764d78748df20daf2b317e9"
+  integrity sha512-IHUfxSEDS9dDGqYwIW7wTN6tn/O8E0n5PcAHz9cAaBoZw6UpG20IG/YM3NNLaGPwPqgjBAFjIURzqoQs3rrtuw==
+
+"@fortawesome/fontawesome-svg-core@1.2.35":
+  version "1.2.35"
+  resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.35.tgz#85aea8c25645fcec88d35f2eb1045c38d3e65cff"
+  integrity sha512-uLEXifXIL7hnh2sNZQrIJWNol7cTVIzwI+4qcBIq9QWaZqUblm0IDrtSqbNg+3SQf8SMGHkiSigD++rHmCHjBg==
   dependencies:
-    "@fortawesome/fontawesome-common-types" "^0.2.12"
+    "@fortawesome/fontawesome-common-types" "^0.2.35"
 
 "@fortawesome/free-solid-svg-icons@5.6.3":
   version "5.6.3"
   dependencies:
     "@fortawesome/fontawesome-common-types" "^0.2.12"
 
-"@fortawesome/react-fontawesome@0.1.3":
-  version "0.1.3"
-  resolved "https://registry.yarnpkg.com/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.3.tgz#266b4047892c3d10498af1075d89252f74015b11"
-  integrity sha512-tc689l67rPZ7+ynZVUgOXY80rAt5KxvuH1qjPpJcbyJzJHzk5yhrD993BjsSEdPBLTtPqmvwynsO/XrAQqHbtg==
+"@fortawesome/react-fontawesome@0.1.14":
+  version "0.1.14"
+  resolved "https://registry.yarnpkg.com/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.14.tgz#bf28875c3935b69ce2dc620e1060b217a47f64ca"
+  integrity sha512-4wqNb0gRLVaBm/h+lGe8UfPPivcbuJ6ecI4hIgW0LjI7kzpYB9FkN0L9apbVzg+lsBdcTf0AlBtODjcSX5mmKA==
   dependencies:
-    humps "^2.0.1"
-    prop-types "^15.5.10"
+    prop-types "^15.7.2"
 
 "@lerna/add@3.21.0":
   version "3.21.0"
     react-is "^16.8.0"
 
 "@material-ui/styles@^4.10.0":
-  version "4.10.0"
-  resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.10.0.tgz#2406dc23aa358217aa8cc772e6237bd7f0544071"
-  integrity sha512-XPwiVTpd3rlnbfrgtEJ1eJJdFCXZkHxy8TrdieaTvwxNYj42VnnCyFzxYeNW9Lhj4V1oD8YtQ6S5Gie7bZDf7Q==
+  version "4.11.4"
+  resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.11.4.tgz#eb9dfccfcc2d208243d986457dff025497afa00d"
+  integrity sha512-KNTIZcnj/zprG5LW0Sao7zw+yG3O35pviHzejMdcSGCdWbiO8qzRgOYL8JAxAsWBKOKYwVZxXtHWaB5T2Kvxew==
   dependencies:
     "@babel/runtime" "^7.4.4"
     "@emotion/hash" "^0.8.0"
-    "@material-ui/types" "^5.1.0"
-    "@material-ui/utils" "^4.9.6"
+    "@material-ui/types" "5.1.0"
+    "@material-ui/utils" "^4.11.2"
     clsx "^1.0.4"
     csstype "^2.5.2"
     hoist-non-react-statics "^3.3.2"
-    jss "^10.0.3"
-    jss-plugin-camel-case "^10.0.3"
-    jss-plugin-default-unit "^10.0.3"
-    jss-plugin-global "^10.0.3"
-    jss-plugin-nested "^10.0.3"
-    jss-plugin-props-sort "^10.0.3"
-    jss-plugin-rule-value-function "^10.0.3"
-    jss-plugin-vendor-prefixer "^10.0.3"
+    jss "^10.5.1"
+    jss-plugin-camel-case "^10.5.1"
+    jss-plugin-default-unit "^10.5.1"
+    jss-plugin-global "^10.5.1"
+    jss-plugin-nested "^10.5.1"
+    jss-plugin-props-sort "^10.5.1"
+    jss-plugin-rule-value-function "^10.5.1"
+    jss-plugin-vendor-prefixer "^10.5.1"
     prop-types "^15.7.2"
 
 "@material-ui/system@^4.9.14":
-  version "4.9.14"
-  resolved "https://registry.yarnpkg.com/@material-ui/system/-/system-4.9.14.tgz#4b00c48b569340cefb2036d0596b93ac6c587a5f"
-  integrity sha512-oQbaqfSnNlEkXEziDcJDDIy8pbvwUmZXWNqlmIwDqr/ZdCK8FuV3f4nxikUh7hvClKV2gnQ9djh5CZFTHkZj3w==
+  version "4.11.3"
+  resolved "https://registry.yarnpkg.com/@material-ui/system/-/system-4.11.3.tgz#466bc14c9986798fd325665927c963eb47cc4143"
+  integrity sha512-SY7otguNGol41Mu2Sg6KbBP1ZRFIbFLHGK81y4KYbsV2yIcaEPOmsCK6zwWlp+2yTV3J/VwT6oSBARtGIVdXPw==
   dependencies:
     "@babel/runtime" "^7.4.4"
-    "@material-ui/utils" "^4.9.6"
+    "@material-ui/utils" "^4.11.2"
     csstype "^2.5.2"
     prop-types "^15.7.2"
 
-"@material-ui/types@^5.1.0":
+"@material-ui/types@5.1.0", "@material-ui/types@^5.1.0":
   version "5.1.0"
   resolved "https://registry.yarnpkg.com/@material-ui/types/-/types-5.1.0.tgz#efa1c7a0b0eaa4c7c87ac0390445f0f88b0d88f2"
   integrity sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A==
 
-"@material-ui/utils@^4.10.2", "@material-ui/utils@^4.7.1", "@material-ui/utils@^4.9.6":
-  version "4.10.2"
-  resolved "https://registry.yarnpkg.com/@material-ui/utils/-/utils-4.10.2.tgz#3fd5470ca61b7341f1e0468ac8f29a70bf6df321"
-  integrity sha512-eg29v74P7W5r6a4tWWDAAfZldXIzfyO1am2fIsC39hdUUHm/33k6pGOKPbgDjg/U/4ifmgAePy/1OjkKN6rFRw==
+"@material-ui/utils@^4.10.2", "@material-ui/utils@^4.11.2", "@material-ui/utils@^4.7.1":
+  version "4.11.2"
+  resolved "https://registry.yarnpkg.com/@material-ui/utils/-/utils-4.11.2.tgz#f1aefa7e7dff2ebcb97d31de51aecab1bb57540a"
+  integrity sha512-Uul8w38u+PICe2Fg2pDKCaIG7kOyhowZ9vjiC1FsVwPABTW8vPPKfF6OvxRq3IiBaI1faOJmgdvMG7rMJARBhA==
   dependencies:
     "@babel/runtime" "^7.4.4"
     prop-types "^15.7.2"
-    react-is "^16.8.0"
+    react-is "^16.8.0 || ^17.0.0"
 
 "@mrmlnc/readdir-enhanced@^2.2.1":
   version "2.2.1"
@@ -3401,6 +3405,11 @@ chart.js@2.8.0:
     chartjs-color "^2.1.0"
     moment "^2.10.2"
 
+chart.js@^3.4.0:
+  version "3.4.0"
+  resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-3.4.0.tgz#4fb2a750225fcc1b387221422f5d4260b55b4579"
+  integrity sha512-mJsRm2apQm5mwz2OgYqGNG4erZh/qljcRZkWSa0kLkFr3UC3e1wKRMgnIh6WdhUrNu0w/JT9PkjLyylqEqHXEQ==
+
 chartjs-color-string@^0.6.0:
   version "0.6.0"
   resolved "https://registry.yarnpkg.com/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz#1df096621c0e70720a64f4135ea171d051402f71"
@@ -5971,11 +5980,6 @@ humanize-ms@^1.2.1:
   dependencies:
     ms "^2.0.0"
 
-humps@^2.0.1:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/humps/-/humps-2.0.1.tgz#dd02ea6081bd0568dc5d073184463957ba9ef9aa"
-  integrity sha1-3QLqYIG9BWjcXQcxhEY5V7qe+ao=
-
 hyphenate-style-name@^1.0.3:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz#691879af8e220aea5750e8827db4ef62a54e361d"
@@ -6087,6 +6091,13 @@ in-publish@^2.0.0:
   resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.1.tgz#948b1a535c8030561cea522f73f78f4be357e00c"
   integrity sha512-oDM0kUSNFC31ShNxHKUyfZKy8ZeXZBWMjMdZHKLOk13uvT27VTL/QzRGfRUcevJhpkZAvlhPYuXkF7eNWrtyxQ==
 
+indefinite-observable@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/indefinite-observable/-/indefinite-observable-2.0.1.tgz#574af29bfbc17eb5947793797bddc94c9d859400"
+  integrity sha512-G8vgmork+6H9S8lUAg1gtXEj2JxIQTo0g2PbFiYOdjkziSI0F7UYBiVwhZRuixhBCNGczAls34+5HJPyZysvxQ==
+  dependencies:
+    symbol-observable "1.2.0"
+
 indent-string@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80"
@@ -7141,65 +7152,65 @@ jsprim@^1.2.2:
     json-schema "0.2.3"
     verror "1.10.0"
 
-jss-plugin-camel-case@^10.0.3:
-  version "10.4.0"
-  resolved "https://registry.yarnpkg.com/jss-plugin-camel-case/-/jss-plugin-camel-case-10.4.0.tgz#46c75ff7fd61c304984c21af5817823f0f501ceb"
-  integrity sha512-9oDjsQ/AgdBbMyRjc06Kl3P8lDCSEts2vYZiPZfGAxbGCegqE4RnMob3mDaBby5H9vL9gWmyyImhLRWqIkRUCw==
+jss-plugin-camel-case@^10.5.1:
+  version "10.6.0"
+  resolved "https://registry.yarnpkg.com/jss-plugin-camel-case/-/jss-plugin-camel-case-10.6.0.tgz#93d2cd704bf0c4af70cc40fb52d74b8a2554b170"
+  integrity sha512-JdLpA3aI/npwj3nDMKk308pvnhoSzkW3PXlbgHAzfx0yHWnPPVUjPhXFtLJzgKZge8lsfkUxvYSQ3X2OYIFU6A==
   dependencies:
     "@babel/runtime" "^7.3.1"
     hyphenate-style-name "^1.0.3"
-    jss "10.4.0"
+    jss "10.6.0"
 
-jss-plugin-default-unit@^10.0.3:
-  version "10.4.0"
-  resolved "https://registry.yarnpkg.com/jss-plugin-default-unit/-/jss-plugin-default-unit-10.4.0.tgz#2b10f01269eaea7f36f0f5fd1cfbfcc76ed42854"
-  integrity sha512-BYJ+Y3RUYiMEgmlcYMLqwbA49DcSWsGgHpVmEEllTC8MK5iJ7++pT9TnKkKBnNZZxTV75ycyFCR5xeLSOzVm4A==
+jss-plugin-default-unit@^10.5.1:
+  version "10.6.0"
+  resolved "https://registry.yarnpkg.com/jss-plugin-default-unit/-/jss-plugin-default-unit-10.6.0.tgz#af47972486819b375f0f3a9e0213403a84b5ef3b"
+  integrity sha512-7y4cAScMHAxvslBK2JRK37ES9UT0YfTIXWgzUWD5euvR+JR3q+o8sQKzBw7GmkQRfZijrRJKNTiSt1PBsLI9/w==
   dependencies:
     "@babel/runtime" "^7.3.1"
-    jss "10.4.0"
+    jss "10.6.0"
 
-jss-plugin-global@^10.0.3:
-  version "10.4.0"
-  resolved "https://registry.yarnpkg.com/jss-plugin-global/-/jss-plugin-global-10.4.0.tgz#19449425a94e4e74e113139b629fd44d3577f97d"
-  integrity sha512-b8IHMJUmv29cidt3nI4bUI1+Mo5RZE37kqthaFpmxf5K7r2aAegGliAw4hXvA70ca6ckAoXMUl4SN/zxiRcRag==
+jss-plugin-global@^10.5.1:
+  version "10.6.0"
+  resolved "https://registry.yarnpkg.com/jss-plugin-global/-/jss-plugin-global-10.6.0.tgz#3e8011f760f399cbadcca7f10a485b729c50e3ed"
+  integrity sha512-I3w7ji/UXPi3VuWrTCbHG9rVCgB4yoBQLehGDTmsnDfXQb3r1l3WIdcO8JFp9m0YMmyy2CU7UOV6oPI7/Tmu+w==
   dependencies:
     "@babel/runtime" "^7.3.1"
-    jss "10.4.0"
+    jss "10.6.0"
 
-jss-plugin-nested@^10.0.3:
-  version "10.4.0"
-  resolved "https://registry.yarnpkg.com/jss-plugin-nested/-/jss-plugin-nested-10.4.0.tgz#017d0c02c0b6b454fd9d7d3fc33470a15eea9fd1"
-  integrity sha512-cKgpeHIxAP0ygeWh+drpLbrxFiak6zzJ2toVRi/NmHbpkNaLjTLgePmOz5+67ln3qzJiPdXXJB1tbOyYKAP4Pw==
+jss-plugin-nested@^10.5.1:
+  version "10.6.0"
+  resolved "https://registry.yarnpkg.com/jss-plugin-nested/-/jss-plugin-nested-10.6.0.tgz#5f83c5c337d3b38004834e8426957715a0251641"
+  integrity sha512-fOFQWgd98H89E6aJSNkEh2fAXquC9aZcAVjSw4q4RoQ9gU++emg18encR4AT4OOIFl4lQwt5nEyBBRn9V1Rk8g==
   dependencies:
     "@babel/runtime" "^7.3.1"
-    jss "10.4.0"
+    jss "10.6.0"
     tiny-warning "^1.0.2"
 
-jss-plugin-props-sort@^10.0.3:
-  version "10.4.0"
-  resolved "https://registry.yarnpkg.com/jss-plugin-props-sort/-/jss-plugin-props-sort-10.4.0.tgz#7110bf0b6049cc2080b220b506532bf0b70c0e07"
-  integrity sha512-j/t0R40/2fp+Nzt6GgHeUFnHVY2kPGF5drUVlgkcwYoHCgtBDOhTTsOfdaQFW6sHWfoQYgnGV4CXdjlPiRrzwA==
+jss-plugin-props-sort@^10.5.1:
+  version "10.6.0"
+  resolved "https://registry.yarnpkg.com/jss-plugin-props-sort/-/jss-plugin-props-sort-10.6.0.tgz#297879f35f9fe21196448579fee37bcde28ce6bc"
+  integrity sha512-oMCe7hgho2FllNc60d9VAfdtMrZPo9n1Iu6RNa+3p9n0Bkvnv/XX5San8fTPujrTBScPqv9mOE0nWVvIaohNuw==
   dependencies:
     "@babel/runtime" "^7.3.1"
-    jss "10.4.0"
+    jss "10.6.0"
 
-jss-plugin-rule-value-function@^10.0.3:
-  version "10.4.0"
-  resolved "https://registry.yarnpkg.com/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.4.0.tgz#7cff4a91e84973536fa49b6ebbdbf7f339b01c82"
-  integrity sha512-w8504Cdfu66+0SJoLkr6GUQlEb8keHg8ymtJXdVHWh0YvFxDG2l/nS93SI5Gfx0fV29dO6yUugXnKzDFJxrdFQ==
+jss-plugin-rule-value-function@^10.5.1:
+  version "10.6.0"
+  resolved "https://registry.yarnpkg.com/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.6.0.tgz#3c1a557236a139d0151e70a82c810ccce1c1c5ea"
+  integrity sha512-TKFqhRTDHN1QrPTMYRlIQUOC2FFQb271+AbnetURKlGvRl/eWLswcgHQajwuxI464uZk91sPiTtdGi7r7XaWfA==
   dependencies:
     "@babel/runtime" "^7.3.1"
-    jss "10.4.0"
+    jss "10.6.0"
     tiny-warning "^1.0.2"
 
-jss-plugin-vendor-prefixer@^10.0.3:
-  version "10.4.0"
-  resolved "https://registry.yarnpkg.com/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.4.0.tgz#2a78f3c5d57d1e024fe7ad7c41de34d04e72ecc0"
-  integrity sha512-DpF+/a+GU8hMh/948sBGnKSNfKkoHg2p9aRFUmyoyxgKjOeH9n74Ht3Yt8lOgdZsuWNJbPrvaa3U4PXKwxVpTQ==
+jss-plugin-vendor-prefixer@^10.5.1:
+  version "10.6.0"
+  resolved "https://registry.yarnpkg.com/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.6.0.tgz#e1fcd499352846890c38085b11dbd7aa1c4f2c78"
+  integrity sha512-doJ7MouBXT1lypLLctCwb4nJ6lDYqrTfVS3LtXgox42Xz0gXusXIIDboeh6UwnSmox90QpVnub7au8ybrb0krQ==
   dependencies:
     "@babel/runtime" "^7.3.1"
     css-vendor "^2.0.8"
-    jss "10.4.0"
+    jss "10.6.0"
 
 jss@10.0.3:
   version "10.0.3"
@@ -7211,13 +7222,14 @@ jss@10.0.3:
     is-in-browser "^1.1.3"
     tiny-warning "^1.0.2"
 
-jss@10.4.0, jss@^10.0.3:
-  version "10.4.0"
-  resolved "https://registry.yarnpkg.com/jss/-/jss-10.4.0.tgz#473a6fbe42e85441020a07e9519dac1e8a2e79ca"
-  integrity sha512-l7EwdwhsDishXzqTc3lbsbyZ83tlUl5L/Hb16pHCvZliA9lRDdNBZmHzeJHP0sxqD0t1mrMmMR8XroR12JBYzw==
+jss@10.6.0, jss@^10.5.1:
+  version "10.6.0"
+  resolved "https://registry.yarnpkg.com/jss/-/jss-10.6.0.tgz#d92ff9d0f214f65ca1718591b68e107be4774149"
+  integrity sha512-n7SHdCozmxnzYGXBHe0NsO0eUf9TvsHVq2MXvi4JmTn3x5raynodDVE/9VQmBdWFyyj9HpHZ2B4xNZ7MMy7lkw==
   dependencies:
     "@babel/runtime" "^7.3.1"
     csstype "^3.0.2"
+    indefinite-observable "^2.0.1"
     is-in-browser "^1.1.3"
     tiny-warning "^1.0.2"
 
@@ -9271,7 +9283,7 @@ promzard@^0.3.0:
   dependencies:
     read "1"
 
-prop-types@15.7.2, prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.2, prop-types@^15.7.2:
+prop-types@15.7.2, prop-types@^15.5.8, prop-types@^15.6.2, prop-types@^15.7.2:
   version "15.7.2"
   resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
   integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
@@ -9479,6 +9491,13 @@ react-chartjs-2@2.7.6:
     lodash "^4.17.4"
     prop-types "^15.5.8"
 
+react-chartjs-2@^3.0.3:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/react-chartjs-2/-/react-chartjs-2-3.0.3.tgz#9cb1981396f9574afebd29aff4e6df71ed615596"
+  integrity sha512-jOFZKwZ8sMLkddewZ/tToxuu4pYimAvvY5I6uK+hCpSFT16Pvo2bdHhUoZ0X87zu9I+dx2I+JCqaLN6XhmrbDg==
+  dependencies:
+    lodash "^4.17.19"
+
 react-dom@17.0.1:
   version "17.0.1"
   resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.1.tgz#1de2560474ec9f0e334285662ede52dbc5426fc6"
@@ -9498,6 +9517,11 @@ react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is
   resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
   integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
 
+"react-is@^16.8.0 || ^17.0.0":
+  version "17.0.2"
+  resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
+  integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
+
 react-router-dom@5.2.0:
   version "5.2.0"
   resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.2.0.tgz#9e65a4d0c45e13289e66c7b17c7e175d0ea15662"
@@ -10822,6 +10846,11 @@ supports-color@^6.1.0:
   dependencies:
     has-flag "^3.0.0"
 
+symbol-observable@1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
+  integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
+
 symbol-tree@^3.2.2:
   version "3.2.4"
   resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"