Odlux Update 89/133689/8
authorsai-neetha <sai-neetha.phulmali@highstreet-technologies.com>
Mon, 20 Mar 2023 07:05:47 +0000 (08:05 +0100)
committerhighstreetherbert <herbert.eiselt@highstreet-technologies.com>
Wed, 29 Mar 2023 17:06:25 +0000 (19:06 +0200)
Add eslint and custom icons update

Issue-ID: CCSDK-3871
Signed-off-by: sai-neetha <sai-neetha.phulmali@highstreet-technologies.com>
Change-Id: If6b676128cc9cff0437a5dc54f85eaafd3b8c586
Signed-off-by: highstreetherbert <herbert.eiselt@highstreet-technologies.com>
272 files changed:
docs/guides/onap-user/mwtnLog.rst [deleted file]
docs/guides/onap-user/mwtnTest.rst [deleted file]
docs/guides/onap-user/pnfEventLog.rst [deleted file]
docs/guides/onap-user/sdnr.rst [deleted file]
docs/tox.ini
sdnr/wt/odlux/.eslintignore [new file with mode: 0644]
sdnr/wt/odlux/apps/apiDemo/package.json
sdnr/wt/odlux/apps/apiDemo/pom.xml
sdnr/wt/odlux/apps/apiDemo/src/handlers/apiDemoRootHandler.ts
sdnr/wt/odlux/apps/apiDemo/src/handlers/modulesHandler.ts
sdnr/wt/odlux/apps/apiDemo/src/models/module.ts
sdnr/wt/odlux/apps/apiDemo/src/plugin.tsx
sdnr/wt/odlux/apps/apiDemo/tsconfig.json
sdnr/wt/odlux/apps/configurationApp/package.json
sdnr/wt/odlux/apps/configurationApp/pom.xml
sdnr/wt/odlux/apps/configurationApp/src/actions/deviceActions.ts
sdnr/wt/odlux/apps/configurationApp/src/assets/icons/configurationAppIcon.svg [new file with mode: 0644]
sdnr/wt/odlux/apps/configurationApp/src/components/baseProps.ts
sdnr/wt/odlux/apps/configurationApp/src/components/ifWhenTextInput.tsx
sdnr/wt/odlux/apps/configurationApp/src/components/uiElementBoolean.tsx
sdnr/wt/odlux/apps/configurationApp/src/components/uiElementLeafList.tsx
sdnr/wt/odlux/apps/configurationApp/src/components/uiElementNumber.tsx
sdnr/wt/odlux/apps/configurationApp/src/components/uiElementReference.tsx
sdnr/wt/odlux/apps/configurationApp/src/components/uiElementSelection.tsx
sdnr/wt/odlux/apps/configurationApp/src/components/uiElementString.tsx
sdnr/wt/odlux/apps/configurationApp/src/components/uiElementUnion.tsx
sdnr/wt/odlux/apps/configurationApp/src/components/verifyer.ts [deleted file]
sdnr/wt/odlux/apps/configurationApp/src/handlers/configurationAppRootHandler.ts
sdnr/wt/odlux/apps/configurationApp/src/handlers/connectedNetworkElementsHandler.ts
sdnr/wt/odlux/apps/configurationApp/src/handlers/deviceDescriptionHandler.ts
sdnr/wt/odlux/apps/configurationApp/src/handlers/valueSelectorHandler.ts
sdnr/wt/odlux/apps/configurationApp/src/handlers/viewDescriptionHandler.ts
sdnr/wt/odlux/apps/configurationApp/src/models/networkElementConnection.ts
sdnr/wt/odlux/apps/configurationApp/src/models/uiModels.ts
sdnr/wt/odlux/apps/configurationApp/src/models/yang.ts
sdnr/wt/odlux/apps/configurationApp/src/pluginConfiguration.tsx
sdnr/wt/odlux/apps/configurationApp/src/services/restServices.ts
sdnr/wt/odlux/apps/configurationApp/src/services/yangService.ts
sdnr/wt/odlux/apps/configurationApp/src/utilities/verifyer.ts [new file with mode: 0644]
sdnr/wt/odlux/apps/configurationApp/src/utilities/viewEngineHelper.ts [new file with mode: 0644]
sdnr/wt/odlux/apps/configurationApp/src/views/configurationApplication.tsx
sdnr/wt/odlux/apps/configurationApp/src/views/networkElementSelector.tsx
sdnr/wt/odlux/apps/configurationApp/src/yang/whenParser.ts [new file with mode: 0644]
sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts
sdnr/wt/odlux/apps/configurationApp/webpack.config.js
sdnr/wt/odlux/apps/connectApp/package.json
sdnr/wt/odlux/apps/connectApp/pom.xml
sdnr/wt/odlux/apps/connectApp/src/actions/commonNetworkElementsActions.ts
sdnr/wt/odlux/apps/connectApp/src/actions/infoNetworkElementActions.ts
sdnr/wt/odlux/apps/connectApp/src/actions/mountedNetworkElementsActions.ts
sdnr/wt/odlux/apps/connectApp/src/actions/networkElementsActions.ts
sdnr/wt/odlux/apps/connectApp/src/actions/tlsKeyActions.ts
sdnr/wt/odlux/apps/connectApp/src/assets/icons/connectAppIcon.svg [new file with mode: 0644]
sdnr/wt/odlux/apps/connectApp/src/components/connectionStatusLog.tsx
sdnr/wt/odlux/apps/connectApp/src/components/editNetworkElementDialog.tsx
sdnr/wt/odlux/apps/connectApp/src/components/infoNetworkElementDialog.tsx
sdnr/wt/odlux/apps/connectApp/src/components/networkElements.tsx
sdnr/wt/odlux/apps/connectApp/src/components/refreshConnectionStatusLogDialog.tsx
sdnr/wt/odlux/apps/connectApp/src/components/refreshNetworkElementsDialog.tsx
sdnr/wt/odlux/apps/connectApp/src/handlers/connectAppRootHandler.ts
sdnr/wt/odlux/apps/connectApp/src/handlers/connectionStatusLogHandler.ts
sdnr/wt/odlux/apps/connectApp/src/handlers/infoNetworkElementHandler.ts
sdnr/wt/odlux/apps/connectApp/src/handlers/networkElementsHandler.ts
sdnr/wt/odlux/apps/connectApp/src/handlers/tlsKeyHandler.ts
sdnr/wt/odlux/apps/connectApp/src/models/connectionStatusLog.ts
sdnr/wt/odlux/apps/connectApp/src/models/guiCutTrough.ts
sdnr/wt/odlux/apps/connectApp/src/models/networkElementBase.ts
sdnr/wt/odlux/apps/connectApp/src/models/networkElementConnection.ts
sdnr/wt/odlux/apps/connectApp/src/models/networkElementConnectionLog.ts
sdnr/wt/odlux/apps/connectApp/src/models/panelId.ts
sdnr/wt/odlux/apps/connectApp/src/models/topologyNetconf.ts
sdnr/wt/odlux/apps/connectApp/src/models/yangCapabilitiesType.ts
sdnr/wt/odlux/apps/connectApp/src/pluginConnect.tsx
sdnr/wt/odlux/apps/connectApp/src/services/connectService.ts
sdnr/wt/odlux/apps/connectApp/src/views/connectView.tsx
sdnr/wt/odlux/apps/connectApp/tsconfig.json
sdnr/wt/odlux/apps/connectApp/webpack.config.js
sdnr/wt/odlux/apps/demoApp/package.json
sdnr/wt/odlux/apps/demoApp/pom.xml
sdnr/wt/odlux/apps/demoApp/src/actions/authorActions.ts
sdnr/wt/odlux/apps/demoApp/src/components/counter.tsx
sdnr/wt/odlux/apps/demoApp/src/handlers/demoAppRootHandler.ts
sdnr/wt/odlux/apps/demoApp/src/handlers/editAuthorHandler.ts
sdnr/wt/odlux/apps/demoApp/src/handlers/listAuthorsHandler.ts
sdnr/wt/odlux/apps/demoApp/src/models/author.ts
sdnr/wt/odlux/apps/demoApp/src/plugin.tsx
sdnr/wt/odlux/apps/demoApp/src/services/authorService.ts
sdnr/wt/odlux/apps/demoApp/src/views/authorsList.tsx
sdnr/wt/odlux/apps/demoApp/src/views/editAuthor.tsx
sdnr/wt/odlux/apps/demoApp/tsconfig.json
sdnr/wt/odlux/apps/eventLogApp/pom.xml
sdnr/wt/odlux/apps/eventLogApp/src/assets/icons/eventLogAppIcon.svg [new file with mode: 0644]
sdnr/wt/odlux/apps/eventLogApp/src/pluginEventLog.tsx
sdnr/wt/odlux/apps/eventLogApp/tsconfig.json
sdnr/wt/odlux/apps/eventLogApp/webpack.config.js
sdnr/wt/odlux/apps/faultApp/package.json
sdnr/wt/odlux/apps/faultApp/pom.xml
sdnr/wt/odlux/apps/faultApp/src/actions/clearStuckAlarmsAction.ts
sdnr/wt/odlux/apps/faultApp/src/actions/panelChangeActions.ts
sdnr/wt/odlux/apps/faultApp/src/actions/statusActions.ts
sdnr/wt/odlux/apps/faultApp/src/assets/icons/faultAppIcon.svg [new file with mode: 0644]
sdnr/wt/odlux/apps/faultApp/src/components/clearStuckAlarmsDialog.tsx
sdnr/wt/odlux/apps/faultApp/src/components/dashboardHome.tsx
sdnr/wt/odlux/apps/faultApp/src/components/faultStatus.tsx
sdnr/wt/odlux/apps/faultApp/src/components/refreshAlarmLogDialog.tsx
sdnr/wt/odlux/apps/faultApp/src/components/refreshCurrentAlarmsDialog.tsx [new file with mode: 0644]
sdnr/wt/odlux/apps/faultApp/src/components/refreshCurrentProblemsDialog.tsx [deleted file]
sdnr/wt/odlux/apps/faultApp/src/handlers/alarmLogEntriesHandler.ts
sdnr/wt/odlux/apps/faultApp/src/handlers/clearStuckAlarmsHandler.ts
sdnr/wt/odlux/apps/faultApp/src/handlers/currentAlarmsHandler.ts [moved from sdnr/wt/odlux/apps/faultApp/src/handlers/currentProblemsHandler.ts with 69% similarity]
sdnr/wt/odlux/apps/faultApp/src/handlers/faultAppRootHandler.ts
sdnr/wt/odlux/apps/faultApp/src/handlers/faultStatusHandler.ts
sdnr/wt/odlux/apps/faultApp/src/handlers/notificationsHandler.ts
sdnr/wt/odlux/apps/faultApp/src/models/fault.ts
sdnr/wt/odlux/apps/faultApp/src/models/panelId.ts
sdnr/wt/odlux/apps/faultApp/src/pluginFault.tsx
sdnr/wt/odlux/apps/faultApp/src/services/faultStatusService.ts
sdnr/wt/odlux/apps/faultApp/src/views/faultApplication.tsx
sdnr/wt/odlux/apps/faultApp/tsconfig.json
sdnr/wt/odlux/apps/faultApp/webpack.config.js
sdnr/wt/odlux/apps/helpApp/pom.xml
sdnr/wt/odlux/apps/helpApp/src/assets/icons/helpAppIcon.svg [new file with mode: 0644]
sdnr/wt/odlux/apps/helpApp/src/components/helpStatus.tsx
sdnr/wt/odlux/apps/helpApp/src/handlers/helpAppRootHandler.ts
sdnr/wt/odlux/apps/helpApp/src/plugin.tsx
sdnr/wt/odlux/apps/helpApp/src/views/helpApplication.tsx
sdnr/wt/odlux/apps/helpApp/src/views/helpTocApp.tsx
sdnr/wt/odlux/apps/helpApp/tsconfig.json
sdnr/wt/odlux/apps/helpApp/webpack.config.js
sdnr/wt/odlux/apps/inventoryApp/pom.xml
sdnr/wt/odlux/apps/inventoryApp/src/actions/inventoryDeviceListActions.ts [new file with mode: 0644]
sdnr/wt/odlux/apps/inventoryApp/src/actions/inventoryTreeActions.ts
sdnr/wt/odlux/apps/inventoryApp/src/actions/panelActions.ts
sdnr/wt/odlux/apps/inventoryApp/src/assets/icons/inventoryAppIcon.svg [new file with mode: 0644]
sdnr/wt/odlux/apps/inventoryApp/src/components/refreshInventoryDialog.tsx
sdnr/wt/odlux/apps/inventoryApp/src/fakeData/index.ts
sdnr/wt/odlux/apps/inventoryApp/src/handlers/connectedNetworkElementsHandler.ts [deleted file]
sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryAppRootHandler.ts
sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryDeviceListActionHandler.ts [new file with mode: 0644]
sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryElementsHandler.ts
sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryTreeHandler.ts
sdnr/wt/odlux/apps/inventoryApp/src/handlers/panelHandler.ts
sdnr/wt/odlux/apps/inventoryApp/src/models/inventory.ts
sdnr/wt/odlux/apps/inventoryApp/src/models/inventoryDeviceListType.ts [new file with mode: 0644]
sdnr/wt/odlux/apps/inventoryApp/src/models/networkElementConnection.ts
sdnr/wt/odlux/apps/inventoryApp/src/models/panelId.ts
sdnr/wt/odlux/apps/inventoryApp/src/pluginInventory.tsx
sdnr/wt/odlux/apps/inventoryApp/src/services/inventoryService.ts
sdnr/wt/odlux/apps/inventoryApp/src/views/dashboard.tsx
sdnr/wt/odlux/apps/inventoryApp/src/views/detail.tsx
sdnr/wt/odlux/apps/inventoryApp/src/views/treeview.tsx
sdnr/wt/odlux/apps/inventoryApp/tsconfig.json
sdnr/wt/odlux/apps/inventoryApp/webpack.config.js
sdnr/wt/odlux/apps/maintenanceApp/package.json
sdnr/wt/odlux/apps/maintenanceApp/pom.xml
sdnr/wt/odlux/apps/maintenanceApp/src/actions/maintenenceActions.ts
sdnr/wt/odlux/apps/maintenanceApp/src/assets/icons/maintenanceAppIcon.svg [new file with mode: 0644]
sdnr/wt/odlux/apps/maintenanceApp/src/components/editMaintenenceEntryDialog.tsx
sdnr/wt/odlux/apps/maintenanceApp/src/components/refreshMaintenanceEntries.tsx
sdnr/wt/odlux/apps/maintenanceApp/src/handlers/maintenanceAppRootHandler.ts
sdnr/wt/odlux/apps/maintenanceApp/src/handlers/maintenanceEntriesHandler.ts [moved from sdnr/wt/odlux/apps/maintenanceApp/src/handlers/maintenenceEntriesHandler.ts with 78% similarity]
sdnr/wt/odlux/apps/maintenanceApp/src/models/maintenanceEntryType.ts [moved from sdnr/wt/odlux/apps/maintenanceApp/src/models/maintenenceEntryType.ts with 88% similarity]
sdnr/wt/odlux/apps/maintenanceApp/src/pluginMaintenance.tsx
sdnr/wt/odlux/apps/maintenanceApp/src/services/maintenenceService.ts
sdnr/wt/odlux/apps/maintenanceApp/src/utils/timeUtils.ts
sdnr/wt/odlux/apps/maintenanceApp/src/views/maintenanceView.tsx [moved from sdnr/wt/odlux/apps/maintenanceApp/src/views/maintenenceView.tsx with 69% similarity]
sdnr/wt/odlux/apps/maintenanceApp/tsconfig.json
sdnr/wt/odlux/apps/maintenanceApp/webpack.config.js
sdnr/wt/odlux/apps/mediatorApp/pom.xml
sdnr/wt/odlux/apps/mediatorApp/src/assets/icons/mediatorAppIcon.svg [new file with mode: 0644]
sdnr/wt/odlux/apps/mediatorApp/src/components/showMeditaorInfoDialog.tsx
sdnr/wt/odlux/apps/mediatorApp/src/plugin.tsx
sdnr/wt/odlux/apps/mediatorApp/src/views/mediatorApplication.tsx
sdnr/wt/odlux/apps/mediatorApp/src/views/mediatorServerSelection.tsx
sdnr/wt/odlux/apps/mediatorApp/tsconfig.json
sdnr/wt/odlux/apps/mediatorApp/webpack.config.js
sdnr/wt/odlux/apps/minimumApp/pom.xml
sdnr/wt/odlux/apps/minimumApp/src/assets/icons/minimumAppIcon.svg [new file with mode: 0644]
sdnr/wt/odlux/apps/minimumApp/src/handlers/minimumAppRootHandler.ts
sdnr/wt/odlux/apps/minimumApp/src/plugin.tsx
sdnr/wt/odlux/apps/minimumApp/tsconfig.json
sdnr/wt/odlux/apps/performanceHistoryApp/pom.xml
sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/deviceListActions.ts
sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/ltpAction.ts
sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/reloadAction.ts
sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/timeChangeAction.ts
sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/toggleActions.ts
sdnr/wt/odlux/apps/performanceHistoryApp/src/assets/icons/performanceHistoryAppIcon.svg [new file with mode: 0644]
sdnr/wt/odlux/apps/performanceHistoryApp/src/components/adaptiveModulation.tsx
sdnr/wt/odlux/apps/performanceHistoryApp/src/components/chartFilter.tsx
sdnr/wt/odlux/apps/performanceHistoryApp/src/components/crossPolarDiscrimination.tsx
sdnr/wt/odlux/apps/performanceHistoryApp/src/components/ltpSelection.tsx
sdnr/wt/odlux/apps/performanceHistoryApp/src/components/performanceData.tsx
sdnr/wt/odlux/apps/performanceHistoryApp/src/components/receiveLevel.tsx
sdnr/wt/odlux/apps/performanceHistoryApp/src/components/signalToInterference.tsx
sdnr/wt/odlux/apps/performanceHistoryApp/src/components/temperature.tsx
sdnr/wt/odlux/apps/performanceHistoryApp/src/components/toggleContainer.tsx
sdnr/wt/odlux/apps/performanceHistoryApp/src/components/transmissionPower.tsx
sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/adaptiveModulationHandler.ts
sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/availableLtpsActionHandler.ts
sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/crossPolarDiscriminationHandler.ts
sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/deviceListActionHandler.ts
sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/performanceDataHandler.ts
sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/performanceHistoryRootHandler.ts
sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/receiveLevelHandler.ts
sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/signalToInterferenceHandler.ts
sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/temperatureHandler.ts
sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/transmissionPowerHandler.ts
sdnr/wt/odlux/apps/performanceHistoryApp/src/models/availableLtps.ts
sdnr/wt/odlux/apps/performanceHistoryApp/src/models/chartTypes.ts
sdnr/wt/odlux/apps/performanceHistoryApp/src/models/crossPolarDiscriminationDataType.ts
sdnr/wt/odlux/apps/performanceHistoryApp/src/models/deviceListType.ts
sdnr/wt/odlux/apps/performanceHistoryApp/src/models/panelId.ts
sdnr/wt/odlux/apps/performanceHistoryApp/src/models/performanceDataType.ts
sdnr/wt/odlux/apps/performanceHistoryApp/src/models/temperatureDataType.ts
sdnr/wt/odlux/apps/performanceHistoryApp/src/models/toggleDataType.ts
sdnr/wt/odlux/apps/performanceHistoryApp/src/models/topologyNetconf.ts
sdnr/wt/odlux/apps/performanceHistoryApp/src/pluginPerformance.tsx
sdnr/wt/odlux/apps/performanceHistoryApp/src/services/performanceHistoryService.ts
sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/chartUtils.tsx
sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/tableUtils.ts
sdnr/wt/odlux/apps/performanceHistoryApp/src/views/performanceHistoryApplication.tsx
sdnr/wt/odlux/apps/performanceHistoryApp/tsconfig.json
sdnr/wt/odlux/apps/performanceHistoryApp/webpack.config.js
sdnr/wt/odlux/eslintrc.json [new file with mode: 0644]
sdnr/wt/odlux/framework/pom.xml
sdnr/wt/odlux/framework/src/actions/settingsAction.ts
sdnr/wt/odlux/framework/src/app.tsx
sdnr/wt/odlux/framework/src/assets/icons/About.svg [new file with mode: 0644]
sdnr/wt/odlux/framework/src/assets/icons/Home.svg [new file with mode: 0644]
sdnr/wt/odlux/framework/src/assets/icons/Menu.svg [new file with mode: 0644]
sdnr/wt/odlux/framework/src/assets/icons/Tools.svg [new file with mode: 0644]
sdnr/wt/odlux/framework/src/assets/icons/User.svg [new file with mode: 0644]
sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.png [new file with mode: 0644]
sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.svg [new file with mode: 0644]
sdnr/wt/odlux/framework/src/components/errorDisplay.tsx
sdnr/wt/odlux/framework/src/components/icons/menuIcon.tsx [new file with mode: 0644]
sdnr/wt/odlux/framework/src/components/logo.tsx
sdnr/wt/odlux/framework/src/components/material-table/index.tsx
sdnr/wt/odlux/framework/src/components/material-table/showColumnDialog.tsx
sdnr/wt/odlux/framework/src/components/material-table/utilities.ts
sdnr/wt/odlux/framework/src/components/material-ui/listItemLink.tsx
sdnr/wt/odlux/framework/src/components/navigationMenu.tsx
sdnr/wt/odlux/framework/src/components/routing/appFrame.tsx
sdnr/wt/odlux/framework/src/components/settings/general.tsx
sdnr/wt/odlux/framework/src/components/titleBar.tsx
sdnr/wt/odlux/framework/src/flux/connect.tsx [moved from sdnr/wt/odlux/framework/src/flux/connect.ts with 69% similarity]
sdnr/wt/odlux/framework/src/flux/store.ts
sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts
sdnr/wt/odlux/framework/src/middleware/logger.ts
sdnr/wt/odlux/framework/src/middleware/navigation.ts
sdnr/wt/odlux/framework/src/services/applicationApi.ts
sdnr/wt/odlux/framework/src/services/applicationManager.ts
sdnr/wt/odlux/framework/src/services/broadcastService.ts
sdnr/wt/odlux/framework/src/services/index.ts
sdnr/wt/odlux/framework/src/services/restService.ts
sdnr/wt/odlux/framework/src/services/storeService.ts [new file with mode: 0644]
sdnr/wt/odlux/framework/src/services/userdataService.ts [moved from sdnr/wt/odlux/framework/src/services/settingsService.ts with 91% similarity]
sdnr/wt/odlux/framework/src/store/applicationStore.ts
sdnr/wt/odlux/framework/src/utilities/logLevel.ts [new file with mode: 0644]
sdnr/wt/odlux/framework/src/views/about.tsx
sdnr/wt/odlux/framework/src/views/frame.tsx
sdnr/wt/odlux/framework/src/views/home.tsx
sdnr/wt/odlux/framework/src/views/login.tsx
sdnr/wt/odlux/framework/src/views/settings.tsx
sdnr/wt/odlux/framework/src2/main/resources/version.json
sdnr/wt/odlux/installer/pom.xml
sdnr/wt/odlux/odlux.properties
sdnr/wt/odlux/package.json
sdnr/wt/odlux/pom.xml
sdnr/wt/odlux/proxy.conf.js [new file with mode: 0644]
sdnr/wt/odlux/yarn.lock

diff --git a/docs/guides/onap-user/mwtnLog.rst b/docs/guides/onap-user/mwtnLog.rst
deleted file mode 100644 (file)
index 3cfd61b..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-.. contents::
-   :depth: 3
-..
-
-Log
-===
-
-The application displays (UX) application logs. SDN-R offer a common log
-server, so that PNFs or other ONAP components could log there data in a
-common way.
diff --git a/docs/guides/onap-user/mwtnTest.rst b/docs/guides/onap-user/mwtnTest.rst
deleted file mode 100644 (file)
index 1c012fc..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-.. contents::
-   :depth: 3
-..
-
-Test
-====
-
-The view offers in a generic way data fetched from ONF-TR-532 devices
-for test and debug purposes.
diff --git a/docs/guides/onap-user/pnfEventLog.rst b/docs/guides/onap-user/pnfEventLog.rst
deleted file mode 100644 (file)
index 036c1f9..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-.. contents::
-   :depth: 3
-..
-
-Event Log
-=========
-
-The 'EventLog' application displays application logs and messages
-automatically created by the different active applications. SDN-R offers
-a common log service so that PNFs or other ONAP components can log their
-data and users can analyze and export the data in a common way.
diff --git a/docs/guides/onap-user/sdnr.rst b/docs/guides/onap-user/sdnr.rst
deleted file mode 100644 (file)
index db690f9..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-.. contents::
-   :depth: 3
-..
-
-SDN controller persona for 'Radio' (SDN-R)
-==========================================
-
-SDN-R adds features and functionality to the OpenDaylight-based ONAP
-controller 'CCSDK/SDNC'. It is built on the Common Controller Framework
-to control and manage wireless resources. Wireless resources are virtual
-network functions (e.g. vBBU, vEPC) or physical network functions (e.g.
-microwave and millimeter wave radios, eNodeB, RRH, DAS equipment).
-
-| SDN-R is integrated into ONAP using DMaaP APIs. It is interfacing with
-  PNFs and VNFs and with other ONAP components, such as A&AI, DCAE and
-  SO.
-| `See abbreviations <abbreviations.md>`__
-
-.. figure:: ./ONAP-SDN-R.png
-   :alt: SDN-R in ONAP
-
-   SDN-R in ONAP
index 4bbc640..7a8b689 100644 (file)
@@ -10,7 +10,7 @@ deps =
     -chttps://raw.githubusercontent.com/openstack/requirements/stable/yoga/upper-constraints.txt
     -chttps://git.onap.org/doc/plain/etc/upper-constraints.onap.txt
 commands =
-    sphinx-build -b html -n -d {envtmpdir}/doctrees ./ {toxinidir}/_build/html
+    sphinx-build -W -b html -n -d {envtmpdir}/doctrees ./ {toxinidir}/_build/html
     echo "Generated docs available in {toxinidir}/_build/html"
 whitelist_externals =
     echo
@@ -21,7 +21,7 @@ whitelist_externals =
 basepython = python3.8
 #deps = -r{toxinidir}/requirements-docs.txt
 commands = echo "Link Checking not enforced"
-#commands = sphinx-build -b linkcheck -d {envtmpdir}/doctrees ./ {toxinidir}/_build/linkcheck
+#commands = sphinx-build -b -W linkcheck -d {envtmpdir}/doctrees ./ {toxinidir}/_build/linkcheck
 whitelist_externals = echo
 
 [testenv:docs-spellcheck]
diff --git a/sdnr/wt/odlux/.eslintignore b/sdnr/wt/odlux/.eslintignore
new file mode 100644 (file)
index 0000000..e5c3940
--- /dev/null
@@ -0,0 +1,12 @@
+**/dist/**
+**/build/**
+**/node_modules/**
+apps/eventApp/**/*
+apps/faultApp/**/*
+apps/helpApp/**/*
+apps/inventoryApp/**/*
+apps/lineOfSightApp/**/*
+apps/maintenanceApp/**/*
+apps/mediatorApp/**/*
+apps/performanceHistoryApp/**/*
+apps/siteManagerApp/**/*
\ No newline at end of file
index f01a396..ff9e3c4 100644 (file)
@@ -23,6 +23,9 @@
   "dependencies": {
     "@emotion/react": "^11.7.0",
     "@emotion/styled": "^11.6.0",
+    "@fortawesome/fontawesome-svg-core": "1.2.35",
+    "@fortawesome/free-solid-svg-icons": "5.6.3",
+    "@fortawesome/react-fontawesome": "0.1.14",
     "@mui/icons-material": "^5.2.0",
     "@mui/material": "^5.2.2",
     "@mui/styles": "^5.2.2",
index b6dc53e..97f7faa 100644 (file)
@@ -19,6 +19,7 @@
   ~ ============LICENSE_END=======================================================
   ~
   -->
+
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
                         <!-- optional: default phase is "generate-resources" -->
                         <phase>initialize</phase>
                         <configuration>
-                            <nodeVersion>v12.13.0</nodeVersion>
+                            <nodeVersion>v12.22.0</nodeVersion>
                             <yarnVersion>v1.22.10</yarnVersion>
                         </configuration>
                     </execution>
index 36688b3..128a032 100644 (file)
 
 import { combineActionHandler } from '../../../../framework/src/flux/middleware';
 
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
 import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
 
 import { moduleHandler, IModules } from './modulesHandler';
 
 export interface IApiDemoStoreState {
-   modules: IModules
+  modules: IModules;
 }
 
 declare module '../../../../framework/src/store/applicationStore' {
   interface IApplicationStoreState {
-    apiDemo: IApiDemoStoreState
+    apiDemo: IApiDemoStoreState;
   }
 }
 
 const actionHandlers = {
-   modules: moduleHandler
+  modules: moduleHandler,
 };
 
 export const apiDemoRootHandler = combineActionHandler<IApiDemoStoreState>(actionHandlers);
index 8777355..1984a2d 100644 (file)
@@ -20,7 +20,7 @@ import { IActionHandler } from '../../../../framework/src/flux/action';
 import { ModulesRequestSuccess } from '../actions/modulesSuccess';
 import { Module } from '../models/module';
 
-export type IModules = Module[]
+export type IModules = Module[];
 
 const modulesInit: IModules = [];
 
index abf86b7..48772a7 100644 (file)
@@ -19,10 +19,10 @@ export type Module = {
   name: string;
   revision: string; 
   namespace: string;
-}
+};
 
 export type ModuleResult = {
   modules: {
-    module: Module[]
-  }
-}
\ No newline at end of file
+    module: Module[];
+  };
+};
\ No newline at end of file
index 076eb5c..2f70d8e 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from "react";
-import { withRouter, RouteComponentProps, Route, Switch, Redirect } from 'react-router-dom';
+import React from 'react';
+import { withRouter, RouteComponentProps } from 'react-router-dom';
 
-import { faNewspaper } from '@fortawesome/free-solid-svg-icons';
+import { faNewspaper } from '@fortawesome/free-solid-svg-icons/faNewspaper';
 
 import applicationManager from '../../../framework/src/services/applicationManager';
-import connect, { Connect } from '../../../framework/src/flux/connect';
+import { connect, Connect } from '../../../framework/src/flux/connect';
 import { ApiAction } from '../../../framework/src/middleware/api'; // for RestConf
 
 import { apiDemoRootHandler } from './handlers/apiDemoRootHandler';
 import { ModulesRequestSuccess } from './actions/modulesSuccess';
 import { Module } from './models/module';
 
-type AppProps = RouteComponentProps & Connect & { modules: Module[], requestModules: () => void };
+type AppProps = RouteComponentProps & Connect & { modules: Module[]; requestModules: () => void };
 
 const App = (props: AppProps ) => (
   <>
@@ -38,16 +38,16 @@ const App = (props: AppProps ) => (
 );
 
 const FinalApp = withRouter(connect((state) => ({
-  modules: state.apiDemo.modules
+  modules: state.apiDemo.modules,
 }), (dispatcher => ({
-  requestModules: () => { dispatcher.dispatch(new ApiAction('restconf/modules', ModulesRequestSuccess, true)) }
+  requestModules: () => { dispatcher.dispatch(new ApiAction('restconf/modules', ModulesRequestSuccess, true)); },
 })))(App));
 
 applicationManager.registerApplication({
-  name: "apiDemo",
+  name: 'apiDemo',
   icon: faNewspaper,
   rootComponent: FinalApp,
   rootActionHandler: apiDemoRootHandler,
-  menuEntry: "API Demo"
+  menuEntry: 'API Demo',
 });
 
index a66b5d8..ca65092 100644 (file)
@@ -4,7 +4,7 @@
     "outDir": "./dist",
     "sourceMap": true,
     "forceConsistentCasingInFileNames": true,
-    "allowSyntheticDefaultImports": false,
+    "allowSyntheticDefaultImports": true,
     "allowUnreachableCode": false,
     "allowUnusedLabels": false,
     "noFallthroughCasesInSwitch": true,
index afd5456..b1d7d95 100644 (file)
     "@mui/icons-material": "^5.2.0",
     "@mui/material": "^5.2.2",
     "@mui/styles": "^5.2.2",
-    "@odlux/framework": "*"
+    "@odlux/framework": "*",
+    "@fortawesome/fontawesome-svg-core": "1.2.35",
+    "@fortawesome/free-solid-svg-icons": "5.6.3",
+    "@fortawesome/react-fontawesome": "0.1.14",
+    "material-ui-confirm": "3.0.2"
   },
   "peerDependencies": {
     "@types/classnames": "2.2.6",
index 6347562..9703e29 100644 (file)
@@ -19,6 +19,7 @@
   ~ ============LICENSE_END=======================================================
   ~
   -->
+
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
                         <!-- optional: default phase is "generate-resources" -->
                         <phase>initialize</phase>
                         <configuration>
-                            <nodeVersion>v12.13.0</nodeVersion>
+                            <nodeVersion>v12.22.0</nodeVersion>
                             <yarnVersion>v1.22.10</yarnVersion>
                         </configuration>
                     </execution>
index 3758378..5213713 100644 (file)
@@ -1,15 +1,30 @@
 import { Action } from '../../../../framework/src/flux/action';
 import { Dispatch } from '../../../../framework/src/flux/store';
-import { IApplicationStoreState } from "../../../../framework/src/store/applicationStore";
-import { PushAction, ReplaceAction } from "../../../../framework/src/actions/navigationActions";
-import { AddErrorInfoAction } from "../../../../framework/src/actions/errorActions";
+import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
+import { PushAction, ReplaceAction } from '../../../../framework/src/actions/navigationActions';
+import { AddErrorInfoAction } from '../../../../framework/src/actions/errorActions';
 
 import { DisplayModeType, DisplaySpecification } from '../handlers/viewDescriptionHandler';
-import { restService } from "../services/restServices";
-import { YangParser } from "../yang/yangParser";
-import { Module } from "../models/yang";
-import { ViewSpecification, ViewElement, isViewElementReference, isViewElementList, isViewElementObjectOrList, isViewElementRpc, isViewElementChoise, ViewElementChoiseCase, ViewElementString } from "../models/uiModels";
 
+import { restService } from '../services/restServices';
+import { YangParser } from '../yang/yangParser';
+import { Module } from '../models/yang';
+import {
+  ViewSpecification,
+  ViewElement,
+  isViewElementReference,
+  isViewElementList,
+  ViewElementString,
+} from '../models/uiModels';
+
+import {
+  checkResponseCode,
+  splitVPath,
+  filterViewElements,
+  flattenViewElements,
+  getReferencedDataList,
+  resolveViewDescription,
+} from '../utilities/viewEngineHelper';
 
 export class EnableValueSelector extends Action {
   constructor(public listSpecification: ViewSpecification, public listData: any[], public keyProperty: string, public onValueSelected : (value: any) => void ) {
@@ -30,42 +45,26 @@ export class SetSelectedValue extends Action {
 }
 
 export class UpdateDeviceDescription extends Action {
-  constructor( public nodeId: string, public modules: { [name:string]: Module}, public views: ViewSpecification[]) {
+  constructor( public nodeId: string, public modules: { [name:string]: Module }, public views: ViewSpecification[]) {
     super();
   }
 }
 
-export class UpdatViewDescription extends Action {
-  constructor (public vPath: string, public viewData: any, public displaySpecification: DisplaySpecification = { displayMode: DisplayModeType.doNotDisplay }) {
+export class UpdateViewDescription extends Action {
+  constructor(public vPath: string, public viewData: any, public displaySpecification: DisplaySpecification = { displayMode: DisplayModeType.doNotDisplay }) {
     super();
   }
 }
 
-export class UpdatOutputData extends Action {
-  constructor (public outputData: any) {
+export class UpdateOutputData extends Action {
+  constructor(public outputData: any) {
     super();
   }
 }
 
-type HttpResult = {
-  status: number;
-  message?: string | undefined;
-  data: {
-      [key: string]: any;
-  } | null | undefined;
-};
-
-const checkResponseCode = (restResult: HttpResult) =>{
-  
-  //403 gets handled by the framework from now on
-
-  return restResult.status !== 403 && ( restResult.status < 200 || restResult.status > 299);
+export const updateNodeIdAsyncActionCreator = (nodeId: string) => async (dispatch: Dispatch, _getState: () => IApplicationStoreState ) => {
 
-}
-
-export const updateNodeIdAsyncActionCreator = (nodeId: string) => async (dispatch: Dispatch, getState: () => IApplicationStoreState ) => {
-
-  dispatch(new UpdateDeviceDescription("", {}, []));
+  dispatch(new UpdateDeviceDescription('', {}, []));
   dispatch(new SetCollectingSelectionData(true));
   
   const { availableCapabilities, unavailableCapabilities, importOnlyModules } = await restService.getCapabilitiesByMountId(nodeId);
@@ -73,16 +72,24 @@ export const updateNodeIdAsyncActionCreator = (nodeId: string) => async (dispatc
   if (!availableCapabilities || availableCapabilities.length <= 0) {
     dispatch(new SetCollectingSelectionData(false));
     dispatch(new UpdateDeviceDescription(nodeId, {}, []));
-    dispatch(new UpdatViewDescription("", [], {
+    dispatch(new UpdateViewDescription('', [], {
       displayMode: DisplayModeType.displayAsMessage,
-      renderMessage: `NetworkElement : "${nodeId}" has no capabilities.`
+      renderMessage: `NetworkElement : "${nodeId}" has no capabilities.`,
     }));
     throw new Error(`NetworkElement : [${nodeId}] has no capabilities.`);
   }
 
-  const parser = new YangParser(unavailableCapabilities || undefined, importOnlyModules || undefined, nodeId);
+  const parser = new YangParser(
+    nodeId,
+    availableCapabilities.reduce((acc, cur) => {
+      acc[cur.capability] = cur.version;
+      return acc;
+    }, {} as { [key: string]: string }),
+    unavailableCapabilities || undefined,
+    importOnlyModules || undefined,
+  );
 
-  for (let i = 0; i < availableCapabilities.length; ++i){
+  for (let i = 0; i < availableCapabilities.length; ++i) {
     const capRaw = availableCapabilities[i];
     try {
       await parser.addCapability(capRaw.capability, capRaw.version);
@@ -95,184 +102,28 @@ export const updateNodeIdAsyncActionCreator = (nodeId: string) => async (dispatc
 
   dispatch(new SetCollectingSelectionData(false));
 
-  if (process.env.NODE_ENV === "development" ) {
-      console.log(parser, parser.modules, parser.views);
+  if (process.env.NODE_ENV === 'development' ) {
+    console.log(parser, parser.modules, parser.views);
   }
 
   return dispatch(new UpdateDeviceDescription(nodeId, parser.modules, parser.views));
-}
-
-export const splitVPath = (vPath: string, vPathParser : RegExp): [string, string?][] => {
-  const pathParts: [string, string?][] = [];
-  let partMatch: RegExpExecArray | null;
-  if (vPath) do {
-    partMatch = vPathParser.exec(vPath);
-    if (partMatch) {
-      pathParts.push([partMatch[1], partMatch[2] || undefined]);
-    }
-  } while (partMatch)
-  return pathParts;
-}
-
-const getReferencedDataList = async (refPath: string, dataPath: string, modules: { [name: string]: Module }, views: ViewSpecification[]) => {
-  const pathParts = splitVPath(refPath, /(?:(?:([^\/\:]+):)?([^\/]+))/g);  // 1 = opt: namespace / 2 = property
-  const defaultNS = pathParts[0][0];
-  let referencedModule = modules[defaultNS];
-
-  let dataMember: string;
-  let view: ViewSpecification;
-  let currentNS: string | null = null;
-  let dataUrls = [dataPath];
-  let data: any;
-
-  for (let i = 0; i < pathParts.length; ++i) {
-    const [pathPartNS, pathPart] = pathParts[i];
-    const namespace = pathPartNS != null ? (currentNS = pathPartNS) : currentNS;
-
-    const viewElement = i === 0
-      ? views[0].elements[`${referencedModule.name}:${pathPart}`]
-      : view!.elements[`${pathPart}`] || view!.elements[`${namespace}:${pathPart}`];
-
-    if (!viewElement) throw new Error(`Could not find ${pathPart} in ${refPath}`);
-    if (i < pathParts.length - 1) {
-      if (!isViewElementObjectOrList(viewElement)) {
-        throw Error(`Module: [${referencedModule.name}].[${viewElement.label}]. Viewelement is not list or object.`);
-      }
-      view = views[+viewElement.viewId];
-      const resultingDataUrls : string[] = [];
-      if (isViewElementList(viewElement)) {
-        for (let j = 0; j < dataUrls.length; ++j) {
-          const dataUrl = dataUrls[j];
-          const restResult = (await restService.getConfigData(dataUrl));
-          if (restResult.data == null || checkResponseCode(restResult)) {
-            const message = restResult.data && restResult.data.errors && restResult.data.errors.error && restResult.data.errors.error[0] && restResult.data.errors.error[0]["error-message"] || "";
-            throw new Error(`Server Error. Status: [${restResult.status}]\n${message || restResult.message || ''}`);
-          }
-
-          let dataRaw = restResult.data[`${defaultNS}:${dataMember!}`];
-          if (dataRaw === undefined) {
-              dataRaw = restResult.data[dataMember!];
-          }
-          dataRaw = dataRaw instanceof Array
-            ? dataRaw[0]
-            : dataRaw;
+};
 
-          data = dataRaw && dataRaw[viewElement.label] || [];
-          const keys: string[] = data.map((entry: { [key: string]: any } )=> entry[viewElement.key!]);
-          resultingDataUrls.push(...keys.map(key => `${dataUrl}/${viewElement.label.replace(/\//ig, "%2F")}=${key.replace(/\//ig, "%2F")}`));
-        }
-        dataMember = viewElement.label;
-      } else {
-        // just a member, not a list
-        const pathSegment = (i === 0
-          ? `/${referencedModule.name}:${viewElement.label.replace(/\//ig, "%2F")}`
-          : `/${viewElement.label.replace(/\//ig, "%2F")}`);
-        resultingDataUrls.push(...dataUrls.map(dataUrl => dataUrl + pathSegment));
-        dataMember = viewElement.label;
-      }
-      dataUrls = resultingDataUrls;
-    } else {
-      data = [];
-      for (let j = 0; j < dataUrls.length; ++j) {
-        const dataUrl = dataUrls[j];
-        const restResult = (await restService.getConfigData(dataUrl));
-        if (restResult.data == null || checkResponseCode(restResult)) {
-          const message = restResult.data && restResult.data.errors && restResult.data.errors.error && restResult.data.errors.error[0] && restResult.data.errors.error[0]["error-message"] || "";
-          throw new Error(`Server Error. Status: [${restResult.status}]\n${message || restResult.message || ''}`);
-        }
-        let dataRaw = restResult.data[`${defaultNS}:${dataMember!}`];
-        if (dataRaw === undefined) {
-            dataRaw = restResult.data[dataMember!];
-        }
-        dataRaw = dataRaw instanceof Array
-          ? dataRaw[0]
-          : dataRaw;
-        data.push(dataRaw);
-      }
-      // BUG UUID ist nicht in den elements enthalten !!!!!!
-      const key = viewElement && viewElement.label || pathPart;
-      return {
-        view: view!,
-        data: data,
-        key: key,
-      };
-    }
+export const postProcessDisplaySpecificationActionCreator = (vPath: string, viewData: any, displaySpecification: DisplaySpecification) => async (dispatch: Dispatch, _getState: () => IApplicationStoreState) => {
+  
+  if (displaySpecification.displayMode === DisplayModeType.displayAsObject) {
+    displaySpecification = {
+      ...displaySpecification,
+      viewSpecification: await filterViewElements(vPath, viewData, displaySpecification.viewSpecification),
+    };  
   }
-  return null;
-}
-
-const resolveViewDescription = (defaultNS: string | null, vPath: string, view: ViewSpecification): ViewSpecification =>{
-
-  // check if-feature | when | and resolve all references.
-  view = { ...view };
-  view.elements = Object.keys(view.elements).reduce<{ [name: string]: ViewElement }>((acc, cur) => {
-    const resolveHistory : ViewElement[] = [];  
-    let elm = view.elements[cur];
-    const key = defaultNS && cur.replace(new RegExp(`^${defaultNS}:`, "i"),"") || cur;
-    while (isViewElementReference(elm)) {
-      const result = (elm.ref(vPath));  
-      if (result) {
-        const [referencedElement, referencedPath] = result;
-        if (resolveHistory.some(hist => hist === referencedElement)) {
-            console.error(`Circle reference found at: ${vPath}`, resolveHistory);
-            break;
-        }
-        elm = referencedElement;
-        vPath = referencedPath;
-        resolveHistory.push(elm);
-      }
-    } 
-    
-    acc[key] = { ...elm, id: key };
-    
-    return acc;
-  }, {});
-  return view;
-}
-
-const flatenViewElements = (defaultNS: string | null, parentPath: string, elements: { [name: string]: ViewElement }, views: ViewSpecification[], currentPath: string ): { [name: string]: ViewElement } => {
-  if (!elements) return {};
-  return Object.keys(elements).reduce<{ [name: string]: ViewElement }>((acc, cur) => {
-    const elm = elements[cur];
 
-    // remove the detault namespace, and only the default namespace, sine it seems that this is also not in the restconf response
-    const elmKey = defaultNS && elm.id.replace(new RegExp(`^${defaultNS}:`, "i"), "") || elm.id;
-    const key = parentPath ? `${parentPath}.${elmKey}` : elmKey;
-
-    if (isViewElementRpc(elm)) {
-      console.warn(`Flaten of RFC not supported ! [${currentPath}][${elm.label}]`);
-      return acc;
-    } else if (isViewElementObjectOrList(elm)) {
-      const view = views[+elm.viewId];
-      const inner = view && flatenViewElements(defaultNS, key, view.elements, views, `${currentPath}/${view.name}`);
-      inner && Object.keys(inner).forEach(k => (acc[k] = inner[k]));
-    } else if (isViewElementChoise(elm)) {
-     acc[key] = {
-        ...elm,
-        id: key,
-        cases: Object.keys(elm.cases).reduce<{ [name: string]: ViewElementChoiseCase }>((accCases, curCases) => {
-          const caseElement = elm.cases[curCases];
-          accCases[curCases] = {
-            ...caseElement,
-            // Hint: do not use key it contains elmKey, which shell be omitted for cases.
-            elements: flatenViewElements(defaultNS, /*key*/ parentPath, caseElement.elements, views, `${currentPath}/${elm.label}`)
-          };
-          return accCases;
-        }, {}),
-      };
-    } else {
-      acc[key] = {
-        ...elm,
-        id: key,
-      };
-    }
-    return acc;
-  }, {});
+  dispatch(new UpdateViewDescription(vPath, viewData, displaySpecification));
 };
 
 export const updateViewActionAsyncCreator = (vPath: string) => async (dispatch: Dispatch, getState: () => IApplicationStoreState) => {
   const pathParts = splitVPath(vPath, /(?:([^\/\["]+)(?:\[([^\]]*)\])?)/g); // 1 = property / 2 = optional key
-  const { configuration: { deviceDescription: { nodeId, modules, views } }, framework: { navigationState } } = getState();
+  const { configuration: { deviceDescription: { nodeId, modules, views } } } = getState();
   let dataPath = `/rests/data/network-topology:network-topology/topology=topology-netconf/node=${nodeId}/yang-ext:mount`;
 
   let inputViewSpecification: ViewSpecification | undefined = undefined;
@@ -291,26 +142,26 @@ export const updateViewActionAsyncCreator = (vPath: string) => async (dispatch:
   try {
     for (let ind = 0; ind < pathParts.length; ++ind) {
       const [property, key] = pathParts[ind];
-      const namespaceInd = property && property.indexOf(":") || -1;
+      const namespaceInd = property && property.indexOf(':') || -1;
       const namespace: string | null = namespaceInd > -1 ? (currentNS = property.slice(0, namespaceInd)) : currentNS;
 
-      if (ind === 0) { defaultNS = namespace };
+      if (ind === 0) { defaultNS = namespace; }
 
       viewElement = viewSpecification.elements[property] || viewSpecification.elements[`${namespace}:${property}`];
-      if (!viewElement) throw Error("Property [" + property + "] does not exist.");
+      if (!viewElement) throw Error('Property [' + property + '] does not exist.');
 
       if (viewElement.isList && !key) {
         if (pathParts.length - 1 > ind) {
           dispatch(new SetCollectingSelectionData(false));
-          throw new Error("No key for list [" + property + "]");
-        } else if (vPath.endsWith("[]") && pathParts.length - 1 === ind) {
+          throw new Error('No key for list [' + property + ']');
+        } else if (vPath.endsWith('[]') && pathParts.length - 1 === ind) {
 
           // empty key is used for new element
-          if (viewElement && "viewId" in viewElement) viewSpecification = views[+viewElement.viewId];
+          if (viewElement && 'viewId' in viewElement) viewSpecification = views[+viewElement.viewId];
           const data = Object.keys(viewSpecification.elements).reduce<{ [name: string]: any }>((acc, cur) => {
             const elm = viewSpecification.elements[cur];
             if (elm.default) {
-              acc[elm.id] = elm.default || ""
+              acc[elm.id] = elm.default || '';
             }
             return acc;
           }, {});
@@ -319,13 +170,13 @@ export const updateViewActionAsyncCreator = (vPath: string) => async (dispatch:
           const ds: DisplaySpecification = {
             displayMode: DisplayModeType.displayAsObject,
             viewSpecification: resolveViewDescription(defaultNS, vPath, viewSpecification),
-            keyProperty: isViewElementList(viewElement!) && viewElement.key || undefined
+            keyProperty: isViewElementList(viewElement!) && viewElement.key || undefined,
           };
 
           // update display specification
-          return dispatch(new UpdatViewDescription(vPath, data, ds));
+          return dispatch(postProcessDisplaySpecificationActionCreator(vPath, data, ds));
         }
-        if (viewElement && isViewElementList(viewElement) && viewSpecification.parentView === "0") {
+        if (viewElement && isViewElementList(viewElement) && viewSpecification.parentView === '0') {
           // check if there is a reference as key
           const listSpecification = views[+viewElement.viewId];
           const keyElement = viewElement.key && listSpecification.elements[viewElement.key];
@@ -338,35 +189,35 @@ export const updateViewActionAsyncCreator = (vPath: string) => async (dispatch:
               throw new Error(`Key property not found for [${keyElement.referencePath}].`);
             }
             dispatch(new EnableValueSelector(refList.view, refList.data, refList.key, (refKey) => {
-              window.setTimeout(() => dispatch(new PushAction(`${vPath}[${refKey.replace(/\//ig, "%2F")}]`)));
+              window.setTimeout(() => dispatch(new PushAction(`${vPath}[${refKey.replace(/\//ig, '%2F')}]`)));
             }));
           } else {
-            // Found a list at root level of a module w/o a refenrece key.
+            // Found a list at root level of a module w/o a reference key.
             dataPath += `?content=config&fields=${encodeURIComponent(viewElement.id)}(${encodeURIComponent(viewElement.key || '')})`; 
             const restResult = (await restService.getConfigData(dataPath));
-            if (restResult && restResult.status === 200 && restResult.data && restResult.data[viewElement.id] ){
-                // spoof the not existing view here
-                const refData = restResult.data[viewElement.id];
-                const refView : ViewSpecification  = {
-                    id: "-1",
-                    canEdit: false,
+            if (restResult && restResult.status === 200 && restResult.data && restResult.data[viewElement.id] ) {
+              // spoof the not existing view here
+              const refData = restResult.data[viewElement.id];
+              const refView : ViewSpecification  = {
+                id: '-1',
+                canEdit: false,
+                config: false,
+                language: 'en-US',
+                elements: {
+                  [viewElement.key!] : { 
+                    uiType: 'string',
                     config: false,
-                    language: "en-US",
-                    elements: {
-                        [viewElement.key!] : { 
-                           uiType: "string",
-                           config: false,
-                           id: viewElement.key,
-                           label: viewElement.key,
-                           isList: true,
-                        } as ViewElementString
-                    }
-                };
-                dispatch(new EnableValueSelector(refView, refData, viewElement.key!, (refKey) => {
-                 window.setTimeout(() => dispatch(new PushAction(`${vPath}[${refKey.replace(/\//ig, "%2F")}]`))); 
-                }));
+                    id: viewElement.key,
+                    label: viewElement.key,
+                    isList: true,
+                  } as ViewElementString,
+                },
+              };
+              dispatch(new EnableValueSelector(refView, refData, viewElement.key!, (refKey) => {
+                window.setTimeout(() => dispatch(new PushAction(`${vPath}[${refKey.replace(/\//ig, '%2F')}]`))); 
+              }));
             } else {
-              throw new Error("Found a list at root level of a module and could not determine the keys.");
+              throw new Error('Found a list at root level of a module and could not determine the keys.');
             }
             dispatch(new SetCollectingSelectionData(false));
           }
@@ -374,31 +225,30 @@ export const updateViewActionAsyncCreator = (vPath: string) => async (dispatch:
         }
         extractList = true;
       } else {
-        // normal case
+        // normal case & replaces unicode %2C if present
+        dataPath += `/${property}${key ? `=${key.replace(/\%2C/g, ',').replace(/\//ig, '%2F')}` : ''}`;
+
         // in case of the root element the required namespace will be added later,
         // while extracting the data
-
-        dataPath += `/${property}${key ? `=${key.replace(/\%2C/g, ",").replace(/\//ig, "%2F")}` : ""}`;
-
         dataMember = namespace === defaultNS
           ? viewElement.label
           : `${namespace}:${viewElement.label}`;
         extractList = false;
       }
 
-      if (viewElement && "viewId" in viewElement) {
+      if (viewElement && 'viewId' in viewElement) {
         viewSpecification = views[+viewElement.viewId];
-      } else if (viewElement.uiType === "rpc") {
+      } else if (viewElement.uiType === 'rpc') {
         viewSpecification = views[+(viewElement.inputViewId || 0)];
 
         // create new instance & flaten
         inputViewSpecification = viewElement.inputViewId != null && {
           ...views[+(viewElement.inputViewId || 0)],
-          elements: flatenViewElements(defaultNS, "", views[+(viewElement.inputViewId || 0)].elements, views, viewElement.label),
+          elements: flattenViewElements(defaultNS, '', views[+(viewElement.inputViewId || 0)].elements, views, viewElement.label),
         } || undefined;
         outputViewSpecification = viewElement.outputViewId != null && {
           ...views[+(viewElement.outputViewId || 0)],
-          elements: flatenViewElements(defaultNS, "", views[+(viewElement.outputViewId || 0)].elements, views, viewElement.label),
+          elements: flattenViewElements(defaultNS, '', views[+(viewElement.outputViewId || 0)].elements, views, viewElement.label),
         } || undefined;
 
       }
@@ -406,7 +256,7 @@ export const updateViewActionAsyncCreator = (vPath: string) => async (dispatch:
 
     let data: any = {};
     // do not get any data from netconf if there is no view specified || this is the root element [0] || this is an rpc
-    if (viewSpecification && !(viewSpecification.id === "0" || viewElement!.uiType === "rpc")) {
+    if (viewSpecification && !(viewSpecification.id === '0' || viewElement!.uiType === 'rpc')) {
       const restResult = (await restService.getConfigData(dataPath));
       if (!restResult.data) {
         // special case: if this is a list without any response
@@ -418,21 +268,21 @@ export const updateViewActionAsyncCreator = (vPath: string) => async (dispatch:
           const ds: DisplaySpecification = {
             displayMode: extractList ? DisplayModeType.displayAsList : DisplayModeType.displayAsObject,
             viewSpecification: resolveViewDescription(defaultNS, vPath, viewSpecification),
-            keyProperty: viewElement.key
+            keyProperty: viewElement.key,
           };
 
           // update display specification
-          return dispatch(new UpdatViewDescription(vPath, [], ds));
+          return dispatch(postProcessDisplaySpecificationActionCreator(vPath, [], ds));
         }
         throw new Error(`Did not get response from Server. Status: [${restResult.status}]`);
       } else if (checkResponseCode(restResult)) {
-        const message = restResult.data.errors && restResult.data.errors.error && restResult.data.errors.error[0] && restResult.data.errors.error[0]["error-message"] || "";
+        const message = restResult.data.errors && restResult.data.errors.error && restResult.data.errors.error[0] && restResult.data.errors.error[0]['error-message'] || '';
         throw new Error(`Server Error. Status: [${restResult.status}]\n${message}`);
       } else {
-        // https://tools.ietf.org/html/rfc7951#section-4 the root element may countain a namesapce or not !  
+        // https://tools.ietf.org/html/rfc7951#section-4 the root element may contain a namespace or not !  
         data = restResult.data[`${defaultNS}:${dataMember!}`];
         if (data === undefined) {
-           data = restResult.data[dataMember!]; // extract dataMember w/o namespace
+          data = restResult.data[dataMember!]; // extract dataMember w/o namespace
         }
       }
 
@@ -446,19 +296,21 @@ export const updateViewActionAsyncCreator = (vPath: string) => async (dispatch:
         ? data[viewElement!.id] || data[viewElement!.label] || [] // if the list is empty, it does not exist
         : data;
 
-    } else if (viewElement! && viewElement!.uiType === "rpc") {
+    } else if (viewElement! && viewElement!.uiType === 'rpc') {
       // set data to defaults
       data = {};
-      inputViewSpecification && Object.keys(inputViewSpecification.elements).forEach(key => {
-        const elm = inputViewSpecification && inputViewSpecification.elements[key];
-        if (elm && elm.default != undefined) {
-          data[elm.id] = elm.default;
-        }
-      });
+      if (inputViewSpecification) {
+        Object.keys(inputViewSpecification.elements).forEach(key => {
+          const elm = inputViewSpecification && inputViewSpecification.elements[key];
+          if (elm && elm.default != undefined) {
+            data[elm.id] = elm.default;
+          }
+        });
+      }
     }
     
     // create display specification
-    const ds: DisplaySpecification = viewElement! && viewElement!.uiType === "rpc"
+    const ds: DisplaySpecification = viewElement! && viewElement!.uiType === 'rpc'
       ? {
         dataPath,
         displayMode: DisplayModeType.displayAsRPC,
@@ -470,16 +322,18 @@ export const updateViewActionAsyncCreator = (vPath: string) => async (dispatch:
         displayMode: extractList ? DisplayModeType.displayAsList : DisplayModeType.displayAsObject,
         viewSpecification: resolveViewDescription(defaultNS, vPath, viewSpecification),
         keyProperty: isViewElementList(viewElement!) && viewElement.key || undefined,
-        apidocPath:  isViewElementList(viewElement!) &&  `/apidoc/explorer/index.html?urls.primaryName=$$$standard$$$#/mounted%20${nodeId}%20${viewElement!.module || 'MODULE_NOT_DEFINED'}/$$$action$$$_${dataPath.replace(/^\//,'').replace(/[\/=\-\:]/g,'_')}_${viewElement! != null ? `${viewElement.id.replace(/[\/=\-\:]/g,'_')}_` : '' }` || undefined,
+        
+        // eslint-disable-next-line max-len
+        apidocPath:  isViewElementList(viewElement!) &&  `/apidoc/explorer/index.html?urls.primaryName=$$$standard$$$#/mounted%20${nodeId}%20${viewElement!.module || 'MODULE_NOT_DEFINED'}/$$$action$$$_${dataPath.replace(/^\//, '').replace(/[\/=\-\:]/g, '_')}_${viewElement! != null ? `${viewElement.id.replace(/[\/=\-\:]/g, '_')}_` : '' }` || undefined,
       };
 
     // update display specification
-    return dispatch(new UpdatViewDescription(vPath, data, ds));
-    // https://beta.just-run.it/#/configuration/Sim12600/core-model:network-element/ltp[LTP-MWPS-TTP-01]
-    // https://beta.just-run.it/#/configuration/Sim12600/core-model:network-element/ltp[LTP-MWPS-TTP-01]/lp
+    return dispatch(postProcessDisplaySpecificationActionCreator(vPath, data, ds));
+    // https://server.com/#/configuration/Sim12600/core-model:network-element/ltp[LTP-MWPS-TTP-01]
+    // https://server.com/#/configuration/Sim12600/core-model:network-element/ltp[LTP-MWPS-TTP-01]/lp
   } catch (error) {
     history.back();
-    dispatch(new AddErrorInfoAction({ title: "Problem", message: error.message || `Could not process ${dataPath}` }));
+    dispatch(new AddErrorInfoAction({ title: 'Problem', message: error.message || `Could not process ${dataPath}` }));
     dispatch(new SetCollectingSelectionData(false));
   } finally {
     return;
@@ -503,63 +357,63 @@ export const updateDataActionAsyncCreator = (vPath: string, data: any) => async
   try {
     for (let ind = 0; ind < pathParts.length; ++ind) {
       let [property, key] = pathParts[ind];
-      const namespaceInd = property && property.indexOf(":") || -1;
+      const namespaceInd = property && property.indexOf(':') || -1;
       const namespace: string | null = namespaceInd > -1 ? (currentNS = property.slice(0, namespaceInd)) : currentNS;
 
-      if (ind === 0) { defaultNS = namespace };
+      if (ind === 0) { defaultNS = namespace; }
       viewElement = viewSpecification.elements[property] || viewSpecification.elements[`${namespace}:${property}`];
-      if (!viewElement) throw Error("Property [" + property + "] does not exist.");
+      if (!viewElement) throw Error('Property [' + property + '] does not exist.');
 
       if (isViewElementList(viewElement) && !key) {
         embedList = true;
-        if (viewElement && viewElement.isList && viewSpecification.parentView === "0") {
-          throw new Error("Found a list at root level of a module w/o a refenrece key.");
+        if (viewElement && viewElement.isList && viewSpecification.parentView === '0') {
+          throw new Error('Found a list at root level of a module w/o a refenrece key.');
         }
         if (pathParts.length - 1 > ind) {
           dispatch(new SetCollectingSelectionData(false));
-          throw new Error("No key for list [" + property + "]");
-        } else if (vPath.endsWith("[]") && pathParts.length - 1 === ind) {
+          throw new Error('No key for list [' + property + ']');
+        } else if (vPath.endsWith('[]') && pathParts.length - 1 === ind) {
           // handle new element with any number of arguments
-          let keyList = viewElement.key?.split(" ");
-          let dataPathParam = keyList?.map(id => data[id]).join(",");
-          key = viewElement.key && String(dataPathParam) || "";
+          let keyList = viewElement.key?.split(' ');
+          let dataPathParam = keyList?.map(id => data[id]).join(',');
+          key = viewElement.key && String(dataPathParam) || '';
           isNew = key;
           if (!key) {
             dispatch(new SetCollectingSelectionData(false));
-            throw new Error("No value for key [" + viewElement.key + "] in list [" + property + "]");
+            throw new Error('No value for key [' + viewElement.key + '] in list [' + property + ']');
           }
         }
       }
 
-      dataPath += `/${property}${key ? `=${key.replace(/\//ig, "%2F")}` : ""}`;
+      dataPath += `/${property}${key ? `=${key.replace(/\//ig, '%2F')}` : ''}`;
       dataMember = viewElement.label;
       embedList = false;
 
-      if (viewElement && "viewId" in viewElement) {
+      if (viewElement && 'viewId' in viewElement) {
         viewSpecification = views[+viewElement.viewId];
       }
     }
 
     // remove read-only elements
-    const removeReadOnlyElements = (viewSpecification: ViewSpecification, isList: boolean, data: any) => {
+    const removeReadOnlyElements = (pViewSpecification: ViewSpecification, isList: boolean, pData: any) => {
       if (isList) {
-        return data.map((elm : any) => removeReadOnlyElements(viewSpecification, false, elm));
+        return pData.map((elm : any) => removeReadOnlyElements(pViewSpecification, false, elm));
       } else {
-        return Object.keys(data).reduce<{[key: string]: any}>((acc, cur)=>{
-          const [nsOrName, name] = cur.split(':',1);
-          const element = viewSpecification.elements[cur] || viewSpecification.elements[nsOrName] || viewSpecification.elements[name];
-          if (!element && process.env.NODE_ENV === "development" ) {
-            throw new Error("removeReadOnlyElements: Could not determine elment for data.");
+        return Object.keys(pData).reduce<{ [key: string]: any }>((acc, cur)=>{
+          const [nsOrName, name] = cur.split(':', 1);
+          const element = pViewSpecification.elements[cur] || pViewSpecification.elements[nsOrName] || pViewSpecification.elements[name];
+          if (!element && process.env.NODE_ENV === 'development' ) {
+            throw new Error('removeReadOnlyElements: Could not determine elment for data.');
           }
           if (element && element.config) {
-            if (element.uiType==="object") {
+            if (element.uiType === 'object') {
               const view = views[+element.viewId];
               if (!view) {
-                throw new Error("removeReadOnlyElements: Internal Error could not determine viewId: "+element.viewId);
+                throw new Error('removeReadOnlyElements: Internal Error could not determine viewId: ' + element.viewId);
               }
-              acc[cur] = removeReadOnlyElements(view, element.isList != null && element.isList, data[cur]);
+              acc[cur] = removeReadOnlyElements(view, element.isList != null && element.isList, pData[cur]);
             } else {
-              acc[cur] = data[cur];
+              acc[cur] = pData[cur];
             }
           }
           return acc;
@@ -580,10 +434,10 @@ export const updateDataActionAsyncCreator = (vPath: string, data: any) => async
       : data;
 
     // do not extract root member (0)
-    if (viewSpecification && viewSpecification.id !== "0") {
+    if (viewSpecification && viewSpecification.id !== '0') {
       const updateResult = await restService.setConfigData(dataPath, { [`${currentNS}:${dataMember!}`]: data }); // addDataMember using currentNS
       if (checkResponseCode(updateResult)) {
-        const message = updateResult.data && updateResult.data.errors && updateResult.data.errors.error && updateResult.data.errors.error[0] && updateResult.data.errors.error[0]["error-message"] || "";
+        const message = updateResult.data && updateResult.data.errors && updateResult.data.errors.error && updateResult.data.errors.error[0] && updateResult.data.errors.error[0]['error-message'] || '';
         throw new Error(`Server Error. Status: [${updateResult.status}]\n${message || updateResult.message || ''}`);
       }
     }
@@ -600,10 +454,10 @@ export const updateDataActionAsyncCreator = (vPath: string, data: any) => async
     };
 
     // update display specification
-    return dispatch(new UpdatViewDescription(vPath, data, ds));
+    return dispatch(new UpdateViewDescription(vPath, data, ds));
   } catch (error) {
     history.back();
-    dispatch(new AddErrorInfoAction({ title: "Problem", message: error.message || `Could not change ${dataPath}` }));
+    dispatch(new AddErrorInfoAction({ title: 'Problem', message: error.message || `Could not change ${dataPath}` }));
 
   } finally {
     dispatch(new SetCollectingSelectionData(false));
@@ -619,57 +473,53 @@ export const removeElementActionAsyncCreator = (vPath: string) => async (dispatc
   let viewElement: ViewElement;
 
   let currentNS: string | null = null;
-  let defaultNS: string | null = null;
-
+  
   dispatch(new SetCollectingSelectionData(true));
   try {
     for (let ind = 0; ind < pathParts.length; ++ind) {
       let [property, key] = pathParts[ind];
-      const namespaceInd = property && property.indexOf(":") || -1;
+      const namespaceInd = property && property.indexOf(':') || -1;
       const namespace: string | null = namespaceInd > -1 ? (currentNS = property.slice(0, namespaceInd)) : currentNS;
 
-      if (ind === 0) { defaultNS = namespace };
       viewElement = viewSpecification.elements[property] || viewSpecification.elements[`${namespace}:${property}`];
-      if (!viewElement) throw Error("Property [" + property + "] does not exist.");
+      if (!viewElement) throw Error('Property [' + property + '] does not exist.');
 
       if (isViewElementList(viewElement) && !key) {
-        if (viewElement && viewElement.isList && viewSpecification.parentView === "0") {
-          throw new Error("Found a list at root level of a module w/o a refenrece key.");
+        if (viewElement && viewElement.isList && viewSpecification.parentView === '0') {
+          throw new Error('Found a list at root level of a module w/o a reference key.');
         }
         if (pathParts.length - 1 > ind) {
           dispatch(new SetCollectingSelectionData(false));
-          throw new Error("No key for list [" + property + "]");
-        } else if (vPath.endsWith("[]") && pathParts.length - 1 === ind) {
+          throw new Error('No key for list [' + property + ']');
+        } else if (vPath.endsWith('[]') && pathParts.length - 1 === ind) {
           // remove the whole table
         }
       }
 
-      dataPath += `/${property}${key ? `=${key.replace(/\//ig, "%2F")}` : ""}`;
+      dataPath += `/${property}${key ? `=${key.replace(/\//ig, '%2F')}` : ''}`;
 
-      if (viewElement && "viewId" in viewElement) {
+      if (viewElement && 'viewId' in viewElement) {
         viewSpecification = views[+viewElement.viewId];
-      } else if (viewElement.uiType === "rpc") {
+      } else if (viewElement.uiType === 'rpc') {
         viewSpecification = views[+(viewElement.inputViewId || 0)];
       }
     }
 
     const updateResult = await restService.removeConfigElement(dataPath);
     if (checkResponseCode(updateResult)) {
-      const message = updateResult.data && updateResult.data.errors && updateResult.data.errors.error && updateResult.data.errors.error[0] && updateResult.data.errors.error[0]["error-message"] || "";
+      const message = updateResult.data && updateResult.data.errors && updateResult.data.errors.error && updateResult.data.errors.error[0] && updateResult.data.errors.error[0]['error-message'] || '';
       throw new Error(`Server Error. Status: [${updateResult.status}]\n${message || updateResult.message || ''}`);
     }
   } catch (error) {
-    dispatch(new AddErrorInfoAction({ title: "Problem", message: error.message || `Could not remove ${dataPath}` }));
+    dispatch(new AddErrorInfoAction({ title: 'Problem', message: error.message || `Could not remove ${dataPath}` }));
   } finally {
     dispatch(new SetCollectingSelectionData(false));
   }
-
-
 };
 
 export const executeRpcActionAsyncCreator = (vPath: string, data: any) => async (dispatch: Dispatch, getState: () => IApplicationStoreState) => {
   const pathParts = splitVPath(vPath, /(?:([^\/\["]+)(?:\[([^\]]*)\])?)/g); // 1 = property / 2 = optional key
-  const { configuration: { deviceDescription: { nodeId, views }, viewDescription: oldViewDescription } } = getState();
+  const { configuration: { deviceDescription: { nodeId, views } } } = getState();
   let dataPath = `/rests/operations/network-topology:network-topology/topology=topology-netconf/node=${nodeId}/yang-ext:mount`;
   let viewSpecification: ViewSpecification = views[0];
   let viewElement: ViewElement;
@@ -684,17 +534,17 @@ export const executeRpcActionAsyncCreator = (vPath: string, data: any) => async
   try {
     for (let ind = 0; ind < pathParts.length; ++ind) {
       let [property, key] = pathParts[ind];
-      const namespaceInd = property && property.indexOf(":") || -1;
+      const namespaceInd = property && property.indexOf(':') || -1;
       const namespace: string | null = namespaceInd > -1 ? (currentNS = property.slice(0, namespaceInd)) : currentNS;
 
-      if (ind === 0) { defaultNS = namespace };
+      if (ind === 0) { defaultNS = namespace; }
       viewElement = viewSpecification.elements[property] || viewSpecification.elements[`${namespace}:${property}`];
-      if (!viewElement) throw Error("Property [" + property + "] does not exist.");
+      if (!viewElement) throw Error('Property [' + property + '] does not exist.');
 
       if (isViewElementList(viewElement) && !key) {
         embedList = true;
       //   if (viewElement && viewElement.isList && viewSpecification.parentView === "0") {
-      //     throw new Error("Found a list at root level of a module w/o a refenrece key.");
+      //     throw new Error("Found a list at root level of a module w/o a reference key.");
       //   }
       //   if (pathParts.length - 1 > ind) {
       //     dispatch(new SetCollectingSelectionData(false));
@@ -710,28 +560,28 @@ export const executeRpcActionAsyncCreator = (vPath: string, data: any) => async
       //   }
       }
 
-      dataPath += `/${property}${key ? `=${key.replace(/\//ig, "%2F")}` : ""}`;
+      dataPath += `/${property}${key ? `=${key.replace(/\//ig, '%2F')}` : ''}`;
       dataMember = viewElement.label;
       embedList = false;
 
-      if (viewElement && "viewId" in viewElement) {
+      if (viewElement && 'viewId' in viewElement) {
         viewSpecification = views[+viewElement.viewId];
-      } else if (viewElement.uiType === "rpc") {
+      } else if (viewElement.uiType === 'rpc') {
         viewSpecification = views[+(viewElement.inputViewId || 0)];
       }
     }
 
     // re-inflate formerly flatten rpc data
     data = data && Object.keys(data).reduce < { [name: string ]: any }>((acc, cur) => {
-      const pathParts = cur.split(".");
+      const innerPathParts = cur.split('.');
       let pos = 0;
       const updatePath = (obj: any, key: string) => {
-        obj[key] = (pos >= pathParts.length)
+        obj[key] = (pos >= innerPathParts.length)
           ? data[cur]
-          : updatePath(obj[key] || {}, pathParts[pos++]);
+          : updatePath(obj[key] || {}, innerPathParts[pos++]);
         return obj;
-      }
-      updatePath(acc, pathParts[pos++]);
+      };
+      updatePath(acc, innerPathParts[pos++]);
       return acc;
     }, {}) || null;
 
@@ -746,22 +596,22 @@ export const executeRpcActionAsyncCreator = (vPath: string, data: any) => async
       : data;
 
     // do not post root member (0)
-    if ((viewSpecification && viewSpecification.id !== "0") || (dataMember! && !data)) {
+    if ((viewSpecification && viewSpecification.id !== '0') || (dataMember! && !data)) {
       const updateResult = await restService.executeRpc(dataPath, { [`${defaultNS}:input`]: data || {} });
       if (checkResponseCode(updateResult)) {
-        const message = updateResult.data && updateResult.data.errors && updateResult.data.errors.error && updateResult.data.errors.error[0] && updateResult.data.errors.error[0]["error-message"] || "";
+        const message = updateResult.data && updateResult.data.errors && updateResult.data.errors.error && updateResult.data.errors.error[0] && updateResult.data.errors.error[0]['error-message'] || '';
         throw new Error(`Server Error. Status: [${updateResult.status}]\n${message || updateResult.message || ''}`);
       }
-      dispatch(new UpdatOutputData(updateResult.data));
+      dispatch(new UpdateOutputData(updateResult.data));
     } else {
-      throw new Error(`There is NO RPC specified.`);
+      throw new Error('There is NO RPC specified.');
     }
 
 
     // // update display specification
     // return
   } catch (error) {
-    dispatch(new AddErrorInfoAction({ title: "Problem", message: error.message || `Could not change ${dataPath}` }));
+    dispatch(new AddErrorInfoAction({ title: 'Problem', message: error.message || `Could not change ${dataPath}` }));
 
   } finally {
     dispatch(new SetCollectingSelectionData(false));
diff --git a/sdnr/wt/odlux/apps/configurationApp/src/assets/icons/configurationAppIcon.svg b/sdnr/wt/odlux/apps/configurationApp/src/assets/icons/configurationAppIcon.svg
new file mode 100644 (file)
index 0000000..1b74cc4
--- /dev/null
@@ -0,0 +1,20 @@
+<!-- highstreet technologies GmbH colour scheme \r
+       Grey    #565656\r
+       LBlue #36A9E1\r
+       DBlue #246DA2\r
+       Green #003F2C / #006C4B\r
+       Yellw #C8D400\r
+       Red     #D81036\r
+-->\r
+\r
+\r
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 142 140" >\r
+<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" transform="scale(10,10)">\r
+\r
+\r
+       <path fill="#565656" d="M7.887,9.025 C7.799,8.449 7.569,7.92 7.229,7.475 L7.995,6.71 L7.307,6.023 L6.536,6.794 C6.093,6.467 5.566,6.245 4.994,6.161 L4.994,5.066 L4.021,5.066 L4.021,6.155 C3.444,6.232 2.913,6.452 2.461,6.777 L1.709,6.024 L1.021,6.712 L1.761,7.452 C1.411,7.901 1.175,8.437 1.087,9.024 L0.062,9.024 L0.062,9.025 L0.062,9.998 L1.08,9.998 C1.162,10.589 1.396,11.132 1.744,11.587 L1.02,12.31 L1.708,12.997 L2.437,12.268 C2.892,12.604 3.432,12.83 4.02,12.91 L4.02,13.958 L4.993,13.958 L4.993,12.904 C5.576,12.818 6.11,12.589 6.56,12.252 L7.306,12.999 L7.994,12.311 L7.248,11.564 C7.586,11.115 7.812,10.581 7.893,10 L8.952,10 L8.952,9.998 L8.952,9.026 L7.887,9.026 L7.887,9.025 Z M4.496,11.295 C3.512,11.295 2.715,10.497 2.715,9.512 C2.715,8.528 3.512,7.73 4.496,7.73 C5.481,7.73 6.28,8.528 6.28,9.512 C6.28,10.497 5.481,11.295 4.496,11.295 L4.496,11.295 Z" ></path>\r
+\r
+       <path fill="#C8D400" d="m 12.231 4.17 l 1.09 -0.281 l -0.252 -0.979 l -1.091 0.282 c -0.118 -0.24 -0.265 -0.47 -0.461 -0.672 c -0.192 -0.196 -0.415 -0.344 -0.647 -0.464 l 0.301 -1.079 l -0.973 -0.271 l -0.299 1.072 c -0.541 -0.043 -1.091 0.078 -1.566 0.382 l -0.76 -0.776 l -0.721 0.707 l 0.756 0.77 c -0.326 0.47 -0.469 1.024 -0.441 1.575 l -1.04 0.268 l 0.252 0.977 l 1.038 -0.268 c 0.117 0.243 0.266 0.475 0.465 0.678 c 0.203 0.208 0.439 0.362 0.686 0.485 l -0.289 1.039 l 0.971 0.271 l 0.293 -1.048 c 0.542 0.033 1.092 -0.1 1.563 -0.415 l 0.771 0.786 l 0.72 -0.707 l -0.776 -0.791 c 0.307 -0.465 0.439 -1.006 0.41 -1.541 l 0 0 z m -2.517 1.617 c -0.823 0 -1.491 -0.669 -1.491 -1.493 c 0 -0.822 0.668 -1.489 1.491 -1.489 c 0.822 0 1.49 0.667 1.49 1.489 c 0 0.824 -0.668 1.493 -1.49 1.493 l 0 0 z" ></path>\r
+\r
+</g>\r
+</svg> 
\ No newline at end of file
index 26c3944..7187c0a 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
-import { ViewElement } from "../models/uiModels";
+import { ViewElement } from '../models/uiModels';
 
 export type BaseProps<TValue = string> = { 
-    value: ViewElement, 
-    inputValue: TValue, 
-    readOnly: boolean, 
-    disabled: boolean, 
-    onChange(newValue: TValue): void;
-    isKey?: boolean 
+  value: ViewElement; 
+  inputValue: TValue; 
+  readOnly: boolean; 
+  disabled: boolean; 
+  onChange(newValue: TValue): void;
+  isKey?: boolean; 
 };
\ No newline at end of file
index 8ce3106..b176e5d 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
-import { ViewElementBase } from "models/uiModels";
-import {
-  TextField,
-  InputAdornment,
-  Input,
-  Tooltip,
-  Divider,
-  IconButton,
-  InputBase,
-  Paper,
-  Theme,
-  FormControl,
-  InputLabel,
-  FormHelperText,
-} from "@mui/material";
+import React from 'react';
+import InputAdornment from '@mui/material/InputAdornment';
+import Input, { InputProps } from '@mui/material/Input';
+import Tooltip from '@mui/material/Tooltip';
+import FormControl from '@mui/material/FormControl';
+import InputLabel from '@mui/material/InputLabel';
+import FormHelperText from '@mui/material/FormHelperText';
+
 import makeStyles from '@mui/styles/makeStyles';
 import createStyles from '@mui/styles/createStyles';
-import * as React from 'react';
-import { faAdjust } from "@fortawesome/free-solid-svg-icons";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { InputProps } from "@mui/material/Input";
 
-const useStyles = makeStyles((theme: Theme) =>
+import { faAdjust } from '@fortawesome/free-solid-svg-icons/faAdjust';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+
+import { ViewElementBase } from '../models/uiModels';
+
+const useStyles = makeStyles(() =>
   createStyles({
     iconDark: {
-      color: '#ff8800'
+      color: '#ff8800',
     },
     iconLight: {
-      color: 'orange'
+      color: 'orange',
     },
     padding: {
       paddingLeft: 10,
-      paddingRight: 10
+      paddingRight: 10,
     },
   }),
 );
 
-type IfwhenProps = InputProps & {
+type IfWhenProps = InputProps & {
   label: string;
   element: ViewElementBase;
   helperText: string;
   error: boolean;
-  onChangeTooltipVisuability(value: boolean): void;
+  onChangeTooltipVisibility(value: boolean): void;
 };
 
-export const IfWhenTextInput = (props: IfwhenProps) => {
+export const IfWhenTextInput = (props: IfWhenProps) => {
 
-  const { element, onChangeTooltipVisuability: toogleTooltip, id, label, helperText: errorText, error, style, ...otherProps } = props;
+  const { element, id, label, helperText: errorText, error, style, ...otherProps } = props;
   const classes = useStyles();
 
-
   const ifFeature = element.ifFeature
     ? (
-        <Tooltip disableInteractive onMouseMove={e => props.onChangeTooltipVisuability(false)} onMouseOut={e => props.onChangeTooltipVisuability(true)} title={element.ifFeature}>
+      <Tooltip
+        title={element.ifFeature}
+        disableInteractive
+        onMouseMove={() => props.onChangeTooltipVisibility(false)}
+        onMouseOut={() => props.onChangeTooltipVisibility(true)}
+      >
           <InputAdornment position="start">
             <FontAwesomeIcon icon={faAdjust} className={classes.iconDark} />
           </InputAdornment>
         </Tooltip>
-      )
+    )
     : null;
 
   const whenFeature = element.when
     ? (
-        <Tooltip disableInteractive className={classes.padding} onMouseMove={() => props.onChangeTooltipVisuability(false)} onMouseOut={() => props.onChangeTooltipVisuability(true)} title={element.when}>
+      <Tooltip
+        title={element.when}
+        disableInteractive
+        className={classes.padding}
+        onMouseMove={() => props.onChangeTooltipVisibility(false)}
+        onMouseOut={() => props.onChangeTooltipVisibility(true)}
+      >
           <InputAdornment className={classes.padding} position="end">
             <FontAwesomeIcon icon={faAdjust} className={classes.iconLight}/>
           </InputAdornment>
         </Tooltip>
-      
+    ) 
     : null;
 
   return (
     <FormControl variant="standard" error={error} style={style}>
       <InputLabel htmlFor={id} >{label}</InputLabel>
-      <Input id={id} inputProps={{'aria-label': label+'-input'}} endAdornment={<div>{ifFeature}{whenFeature}</div>} {...otherProps}  />
+      <Input id={id} inputProps={{ 'aria-label': label + '-input' }} endAdornment={<div>{ifFeature}{whenFeature}</div>} {...otherProps}  />
       <FormHelperText>{errorText}</FormHelperText>
     </FormControl>
   );
-}
\ No newline at end of file
+};
\ No newline at end of file
index 81c9d6d..56fb93c 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
-import * as React from "react"
-import { MenuItem, FormHelperText, Select, FormControl, InputLabel } from "@mui/material";
+import React from 'react';
 
-import { ViewElementBoolean } from "../models/uiModels";
-import { BaseProps } from "./baseProps";
+import MenuItem from '@mui/material/MenuItem';
+import FormHelperText from '@mui/material/FormHelperText';
+import Select from '@mui/material/Select';
+import FormControl from '@mui/material/FormControl';
+import InputLabel from '@mui/material/InputLabel';
+
+import { ViewElementBoolean } from '../models/uiModels';
+import { BaseProps } from './baseProps';
 
 type BooleanInputProps = BaseProps<boolean>;
 
 export const UiElementBoolean = (props: BooleanInputProps) => {
 
-    const element = props.value as ViewElementBoolean;
+  const element = props.value as ViewElementBoolean;
 
-    const value = String(props.inputValue).toLowerCase();
-    const mandetoryError = element.mandatory && value !== 'true' && value !== 'false';
+  const value = String(props.inputValue).toLowerCase();
+  const mandatoryError = element.mandatory && value !== 'true' && value !== 'false';
     
-    return (!props.readOnly || element.id != null
-        ? (<FormControl variant="standard" style={{ width: 485, marginLeft: 20, marginRight: 20 }}>
+  return (!props.readOnly || element.id != null
+    ? (<FormControl variant="standard" style={{ width: 485, marginLeft: 20, marginRight: 20 }}>
             <InputLabel htmlFor={`select-${element.id}`} >{element.label}</InputLabel>
             <Select variant="standard"
-                aria-label={element.label+'-selection'}
+                aria-label={element.label + '-selection'}
                 required={!!element.mandatory}
-                error={mandetoryError}
-                onChange={(e) => { props.onChange(e.target.value === 'true') }}
+                error={mandatoryError}
+                onChange={(e) => { props.onChange(e.target.value === 'true'); }}
                 readOnly={props.readOnly}
                 disabled={props.disabled}
                 value={value}
                 inputProps={{
-                    name: element.id,
-                    id: `select-${element.id}`,
+                  name: element.id,
+                  id: `select-${element.id}`,
                 }}
             >
                 <MenuItem value={'true'} aria-label="true">{element.trueValue || 'True'}</MenuItem>
                 <MenuItem value={'false'} aria-label="false">{element.falseValue || 'False'}</MenuItem>
 
             </Select>
-            <FormHelperText>{mandetoryError ? "Value is mandetory" : ""}</FormHelperText>
+            <FormHelperText>{mandatoryError ? 'Value is mandatory' : ''}</FormHelperText>
         </FormControl>)
-        : null
-    );
-}
\ No newline at end of file
+    : null
+  );
+};
\ No newline at end of file
index 5937ed7..669ddff 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
-import * as React from "react"
-import { FormControl, InputLabel, Paper, Chip, FormHelperText, Dialog, DialogTitle, DialogContentText, DialogActions, Button, DialogContent } from "@mui/material";
+import React from 'react';
+import FormControl from '@mui/material/FormControl';
+import InputLabel from '@mui/material/InputLabel';
+import Chip from '@mui/material/Chip';
+import Dialog from '@mui/material/Dialog';
+import DialogTitle from '@mui/material/DialogTitle';
+import DialogContent from '@mui/material/DialogContent';
+import DialogActions from '@mui/material/DialogActions';
+import Button from '@mui/material/Button';
+
 import makeStyles from '@mui/styles/makeStyles';
 import AddIcon from '@mui/icons-material/Add';
 
 import { Theme } from '@mui/material/styles';
-import { ViewElement } from "../models/uiModels";
-
-import { BaseProps } from "./baseProps";
+import { ViewElement } from '../models/uiModels';
 
-type LeafListProps = BaseProps<any []> & {
-  getEditorForViewElement:  (uiElement: ViewElement) => (null | React.ComponentType<BaseProps<any>>)  
-};
+import { BaseProps } from './baseProps';
 
 const useStyles = makeStyles((theme: Theme) => {
   const light = theme.palette.mode === 'light';
@@ -50,93 +54,93 @@ const useStyles = makeStyles((theme: Theme) => {
       margin: theme.spacing(0.5),
     },
     underline: {
-        '&:after': {
-          borderBottom: `2px solid ${theme.palette.primary.main}`,
-          left: 0,
-          bottom: 0,
-          // Doing the other way around crash on IE 11 "''" https://github.com/cssinjs/jss/issues/242
-          content: '""',
-          position: 'absolute',
-          right: 0,
-          transform: 'scaleX(0)',
-          transition: theme.transitions.create('transform', {
-            duration: theme.transitions.duration.shorter,
-            easing: theme.transitions.easing.easeOut,
-          }),
-          pointerEvents: 'none', // Transparent to the hover style.
-        },
-        '&.Mui-focused:after': {
-          transform: 'scaleX(1)',
-        },
-        '&.Mui-error:after': {
-          borderBottomColor: theme.palette.error.main,
-          transform: 'scaleX(1)', // error is always underlined in red
-        },
-        '&:before': {
+      '&:after': {
+        borderBottom: `2px solid ${theme.palette.primary.main}`,
+        left: 0,
+        bottom: 0,
+        // Doing the other way around crash on IE 11 "''" https://github.com/cssinjs/jss/issues/242
+        content: '""',
+        position: 'absolute',
+        right: 0,
+        transform: 'scaleX(0)',
+        transition: theme.transitions.create('transform', {
+          duration: theme.transitions.duration.shorter,
+          easing: theme.transitions.easing.easeOut,
+        }),
+        pointerEvents: 'none', // Transparent to the hover style.
+      },
+      '&.Mui-focused:after': {
+        transform: 'scaleX(1)',
+      },
+      '&.Mui-error:after': {
+        borderBottomColor: theme.palette.error.main,
+        transform: 'scaleX(1)', // error is always underlined in red
+      },
+      '&:before': {
+        borderBottom: `1px solid ${bottomLineColor}`,
+        left: 0,
+        bottom: 0,
+        // Doing the other way around crash on IE 11 "''" https://github.com/cssinjs/jss/issues/242
+        content: '"\\00a0"',
+        position: 'absolute',
+        right: 0,
+        transition: theme.transitions.create('border-bottom-color', {
+          duration: theme.transitions.duration.shorter,
+        }),
+        pointerEvents: 'none', // Transparent to the hover style.
+      },
+      '&:hover:not($disabled):before': {
+        borderBottom: `2px solid ${theme.palette.text.primary}`,
+        // Reset on touch devices, it doesn't add specificity
+        // eslint-disable-next-line @typescript-eslint/naming-convention
+        '@media (hover: none)': {
           borderBottom: `1px solid ${bottomLineColor}`,
-          left: 0,
-          bottom: 0,
-          // Doing the other way around crash on IE 11 "''" https://github.com/cssinjs/jss/issues/242
-          content: '"\\00a0"',
-          position: 'absolute',
-          right: 0,
-          transition: theme.transitions.create('border-bottom-color', {
-            duration: theme.transitions.duration.shorter,
-          }),
-          pointerEvents: 'none', // Transparent to the hover style.
-        },
-        '&:hover:not($disabled):before': {
-          borderBottom: `2px solid ${theme.palette.text.primary}`,
-          // Reset on touch devices, it doesn't add specificity
-          '@media (hover: none)': {
-            borderBottom: `1px solid ${bottomLineColor}`,
-          },
-        },
-        '&.Mui-disabled:before': {
-          borderBottomStyle: 'dotted',
         },
       },
-  })
+      '&.Mui-disabled:before': {
+        borderBottomStyle: 'dotted',
+      },
+    },
+  });
 });
 
+type LeafListProps = BaseProps<any []> & {
+  getEditorForViewElement:  (uiElement: ViewElement) => (null | React.ComponentType<BaseProps<any>>);  
+};
+
 export const UiElementLeafList = (props: LeafListProps) => {
   const { value: element, inputValue, onChange } = props;
 
   const classes = useStyles();
 
   const [open, setOpen] = React.useState(false);
-  const [editorValue, setEditorValue] = React.useState("");
+  const [editorValue, setEditorValue] = React.useState('');
   const [editorValueIndex, setEditorValueIndex] = React.useState(-1);
   
-
-  const handleClickOpen = () => {
-    setOpen(true);
-  };
-
   const handleClose = () => {
     setOpen(false);
   };
 
   const onApplyButton = () => { 
-     if (editorValue != null && editorValue != "" && editorValueIndex < 0) {
-       props.onChange([
-         ...inputValue,
-         editorValue,
-       ]);
-     } else if (editorValue != null && editorValue != "") {
-       props.onChange([
-         ...inputValue.slice(0, editorValueIndex),
-         editorValue,
-         ...inputValue.slice(editorValueIndex+1),
-       ]);
-     }
-     setOpen(false);
+    if (editorValue != null && editorValue != '' && editorValueIndex < 0) {
+      props.onChange([
+        ...inputValue,
+        editorValue,
+      ]);
+    } else if (editorValue != null && editorValue != '') {
+      props.onChange([
+        ...inputValue.slice(0, editorValueIndex),
+        editorValue,
+        ...inputValue.slice(editorValueIndex + 1),
+      ]);
+    }
+    setOpen(false);
   };
 
   const onDelete = (index : number) => {
     const newValue : any[] = [
       ...inputValue.slice(0, index),
-      ...inputValue.slice(index+1),
+      ...inputValue.slice(index + 1),
     ];
     onChange(newValue);
   };
@@ -151,15 +155,15 @@ export const UiElementLeafList = (props: LeafListProps) => {
         { !props.readOnly ? <li>
           <Chip
             icon={<AddIcon />}
-            label={"Add"}
+            label={'Add'}
             className={classes.chip}
             size="small"
             color="secondary"
             onClick={ () => { 
               setOpen(true); 
-              setEditorValue("");
+              setEditorValue('');
               setEditorValueIndex(-1);
-             
+            } 
             }
           />
         </li> : null }  
@@ -172,24 +176,24 @@ export const UiElementLeafList = (props: LeafListProps) => {
               label={String(val)}
               onDelete={ !props.readOnly ? () => { onDelete(ind); } : undefined }  
               onClick={ !props.readOnly ? () => { 
-                  setOpen(true); 
-                  setEditorValue(val);
-                  setEditorValueIndex(ind);
-                  } : undefined
+                setOpen(true); 
+                setEditorValue(val);
+                setEditorValueIndex(ind);
+              } : undefined
               }   
             />
             </li>
-          ))
+        ))
         }
         </ul>
         {/* <FormHelperText>{ "Value is mandetory"}</FormHelperText> */}
         </FormControl>
         <Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
-          <DialogTitle id="form-dialog-title">{editorValueIndex < 0 ? "Add new value" : "Edit value" } </DialogTitle>
+          <DialogTitle id="form-dialog-title">{editorValueIndex < 0 ? 'Add new value' : 'Edit value' } </DialogTitle>
           <DialogContent>
             { ValueEditor && <ValueEditor 
                 inputValue={ editorValue }
-                value={{ ...element, isList: false}}
+                value={{ ...element, isList: false }}
                 disabled={false}
                 readOnly={props.readOnly}
                 onChange={ setEditorValue }
@@ -197,7 +201,7 @@ export const UiElementLeafList = (props: LeafListProps) => {
           </DialogContent>
           <DialogActions>
             <Button color="inherit" onClick={ handleClose }> Cancel </Button>
-            <Button disabled={editorValue == null || editorValue === "" } onClick={ onApplyButton } color="secondary"> {editorValueIndex < 0 ? "Add" : "Apply"} </Button>
+            <Button disabled={editorValue == null || editorValue === '' } onClick={ onApplyButton } color="secondary"> {editorValueIndex < 0 ? 'Add' : 'Apply'} </Button>
           </DialogActions>
         </Dialog>
       </>
index 76c11f6..b034278 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
-import { ViewElementNumber } from "models/uiModels";
+import React from 'react';
+import { ViewElementNumber } from "../models/uiModels";
 import { Tooltip, InputAdornment } from "@mui/material";
-import * as React from 'react';
 import { BaseProps } from "./baseProps";
 import { IfWhenTextInput } from "./ifWhenTextInput";
-import { checkRange } from "./verifyer";
+import { checkRange } from "../utilities/verifyer";
 
-type numberInputProps = BaseProps<any>;
+type numberInputProps = BaseProps<number>;
 
 export const UiElementNumber = (props: numberInputProps) => {
 
@@ -49,12 +49,12 @@ export const UiElementNumber = (props: numberInputProps) => {
       setError(true);
       setHelperText("Input is not a number.");
     }
-    props.onChange(data);
+    props.onChange(num);
   }
 
   return (
     <Tooltip disableInteractive title={isTooltipVisible ? element.description || '' : ''}>
-      <IfWhenTextInput element={element} onChangeTooltipVisuability={setTooltipVisibility}
+      <IfWhenTextInput element={element} onChangeTooltipVisibility={setTooltipVisibility}
         spellCheck={false} autoFocus margin="dense"
         id={element.id} label={element.label} type="text" value={props.inputValue}
         style={{ width: 485, marginLeft: 20, marginRight: 20 }}
index 9e863f0..e3bb8f0 100644 (file)
  */
 
 import React, { useState } from 'react';
-import { Tooltip, Button, FormControl, Theme } from '@mui/material';
+import { Tooltip, Button, FormControl } from '@mui/material';
 
 import createStyles from '@mui/styles/createStyles';
 import makeStyles from '@mui/styles/makeStyles';
 
 import { ViewElement } from '../models/uiModels';
 
-const useStyles = makeStyles((theme: Theme) => createStyles({
+const useStyles = makeStyles(() => createStyles({
   button: {
-    "justifyContent": "left"
+    'justifyContent': 'left',
   },
 }));
 
@@ -37,16 +37,31 @@ type UIElementReferenceProps = {
 };
 
 export const UIElementReference: React.FC<UIElementReferenceProps> = (props) => {
-  const classes = useStyles();
-  const [disabled, setDisabled] = useState(true);
   const { element } = props;
+  const [disabled, setDisabled] = useState(true);
+  const classes = useStyles();
   return (
-    <FormControl variant="standard" key={element.id} style={{ width: 485, marginLeft: 20, marginRight: 20 }} onMouseDown={(ev) => { ev.preventDefault(); ev.stopPropagation(); ev.button === 1 && setDisabled(!disabled) }}>
+    <FormControl
+      variant="standard"
+      key={element.id}
+      style={{ width: 485, marginLeft: 20, marginRight: 20 }}
+      onMouseDown={(ev) => {
+        ev.preventDefault();
+        ev.stopPropagation();
+        if (ev.button === 1) {
+          setDisabled(!disabled);
+        }
+      }}>
       <Tooltip disableInteractive title={element.description || element.path || ''}>
-        <Button className={classes.button} aria-label={element.label+'-button'} color="secondary" disabled={props.disabled && disabled} onClick={() => {
-          props.onOpenReference(element);
-        }}  >{`${element.label}`}</Button>
+        <Button
+          className={classes.button}
+          aria-label={element.label + '-button'}
+          color="secondary"
+          disabled={props.disabled && disabled}
+          onClick={() => {
+            props.onOpenReference(element);
+          }}  >{`${element.label}`}</Button>
       </Tooltip>
     </FormControl>
   );
-}
\ No newline at end of file
+};
\ No newline at end of file
index fdf8034..ebd04da 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
-import * as React from 'react';
+import React from 'react';
 import { BaseProps } from './baseProps';
-import { ViewElementSelection } from '../models/uiModels'
+import { ViewElementSelection } from '../models/uiModels';
 import { FormControl, InputLabel, Select, FormHelperText, MenuItem, Tooltip } from '@mui/material';
 
 type selectionProps = BaseProps;
 
 export const UiElementSelection = (props: selectionProps) => {
 
-    const element = props.value as ViewElementSelection;
+  const element = props.value as ViewElementSelection;
 
-    let error = "";
-    const value = String(props.inputValue);
-    if (element.mandatory && Boolean(!value)) {
-        error = "Error";
-    }
+  let error = '';
+  const value = String(props.inputValue);
+  if (element.mandatory && Boolean(!value)) {
+    error = 'Error';
+  }
 
-    return (props.readOnly || props.inputValue != null
-        ? (<FormControl variant="standard" style={{ width: 485, marginLeft: 20, marginRight: 20 }}>
-            <InputLabel htmlFor={`select-${element.id}`} >{element.label}</InputLabel>
+  return (props.readOnly || props.inputValue != null
+    ? (<FormControl variant="standard" style={{ width: 485, marginLeft: 20, marginRight: 20 }}>
+         <InputLabel htmlFor={`select-${element.id}`} >{element.label}</InputLabel>
             <Select variant="standard"
                 required={!!element.mandatory}
                 error={!!error}
-                onChange={(e) => { props.onChange(e.target.value as string) }}
+                onChange={(e) => { props.onChange(e.target.value as string); }}
                 readOnly={props.readOnly}
                 disabled={props.disabled}
                 value={value.toString()}
-                aria-label={element.label+'-selection'}
+                aria-label={element.label + '-selection'}
                 inputProps={{
-                    name: element.id,
-                    id: `select-${element.id}`,
+                  name: element.id,
+                  id: `select-${element.id}`,
                 }}
             >
-          {element.options.map(option => (
-            <MenuItem key={option.key} value={option.key} aria-label={option.key}><Tooltip disableInteractive title={option.description || '' }><div style={{width:"100%"}}>{option.key}</div></Tooltip></MenuItem>
-          ))}
+                {element.options.map(option => (
+                    <MenuItem
+                        key={option.key}
+                        value={option.key}
+                        aria-label={option.key}>
+                        <Tooltip disableInteractive title={option.description || ''}>
+                            <div style={{ width: '100%' }}>
+                                {option.key}
+                            </div>
+                        </Tooltip>
+                    </MenuItem>
+                ))}
             </Select>
             <FormHelperText>{error}</FormHelperText>
         </FormControl>)
-        : null
-    );
-}
\ No newline at end of file
+    : null
+  );
+};
\ No newline at end of file
index 4908c41..8381d99 100644 (file)
@@ -21,7 +21,7 @@ import { Tooltip, TextField } from "@mui/material";
 import { ViewElementString } from "../models/uiModels";
 import { BaseProps } from "./baseProps";
 import { IfWhenTextInput } from "./ifWhenTextInput";
-import { checkRange, checkPattern } from "./verifyer";
+import { checkRange, checkPattern } from "../utilities/verifyer";
 
 type stringEntryProps = BaseProps ;
 
@@ -69,7 +69,7 @@ export const UiElementString = (props: stringEntryProps) => {
 
     return (
         <Tooltip disableInteractive title={isTooltipVisible ? element.description || '' : ''}>
-            <IfWhenTextInput element={element} onChangeTooltipVisuability={setTooltipVisibility}
+            <IfWhenTextInput element={element} onChangeTooltipVisibility={setTooltipVisibility}
                 spellCheck={false} autoFocus margin="dense"
                 id={element.id} label={props?.isKey ? "🔑 " + element.label : element.label} type="text" value={props.inputValue}
                 style={{ width: 485, marginLeft: 20, marginRight: 20 }}
index 67cd998..8d232f5 100644 (file)
@@ -21,7 +21,7 @@ import { BaseProps } from './baseProps';
 import { Tooltip } from '@mui/material';
 import { IfWhenTextInput } from './ifWhenTextInput';
 import { ViewElementUnion, isViewElementString, isViewElementNumber, isViewElementObject, ViewElementNumber } from '../models/uiModels';
-import { checkRange, checkPattern } from './verifyer';
+import { checkRange, checkPattern } from '../utilities/verifyer';
 
 type UiElementUnionProps = { isKey: boolean } & BaseProps;
 
@@ -77,7 +77,7 @@ export const UIElementUnion = (props: UiElementUnionProps) => {
   };
 
   return <Tooltip disableInteractive title={isTooltipVisible ? element.description || '' : ''}>
-    <IfWhenTextInput element={element} onChangeTooltipVisuability={setTooltipVisibility}
+    <IfWhenTextInput element={element} onChangeTooltipVisibility={setTooltipVisibility}
       spellCheck={false} autoFocus margin="dense"
       id={element.id} label={props.isKey ? "🔑 " + element.label : element.label} type="text" value={props.inputValue}
       onChange={(e: any) => { verifyValues(e.target.value) }}
diff --git a/sdnr/wt/odlux/apps/configurationApp/src/components/verifyer.ts b/sdnr/wt/odlux/apps/configurationApp/src/components/verifyer.ts
deleted file mode 100644 (file)
index 0a95cd8..0000000
+++ /dev/null
@@ -1,280 +0,0 @@
-/**
- * ============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 { Expression, YangRange, Operator, ViewElementNumber, ViewElementString, isViewElementNumber, isViewElementString } from '../models/uiModels';
-
-export type validated = { isValid: boolean, error?: string }
-
-export type validatedRange = { isValid: boolean, error?: string };
-
-
-const rangeErrorStartNumber = "The entered number must be";
-const rangeErrorinnerMinTextNumber = "greater or equals than";
-const rangeErrorinnerMaxTextNumber = "less or equals than";
-const rangeErrorEndTextNumber = ".";
-
-const rangeErrorStartString = "The entered text must have";
-const rangeErrorinnerMinTextString = "no more than";
-const rangeErrorinnerMaxTextString = "less than";
-const rangeErrorEndTextString = " characters.";
-
-let errorMessageStart = "";
-let errorMessageMiddleMinPart = "";
-let errorMessageMiddleMaxPart = "";
-let errorMessageEnd = "";
-
-
-export function checkRange(element: ViewElementNumber | ViewElementString, data: number): string {
-
-    //let test1: Operator<YangRange> = { operation: "AND", arguments: [{ operation: "OR", arguments: [{ operation: "AND", arguments: [new RegExp("^z", "g"), new RegExp("z$", "g")] }, new RegExp("^abc", "g"), new RegExp("^123", "g")] }, new RegExp("^def", "g"), new RegExp("^ppp", "g"), new RegExp("^aaa", "g")] };
-    //let test1: Operator<YangRange> = { operation: "AND", arguments: [{ operation: "OR", arguments: [{ operation: "AND", arguments: [{ min: -5, max: 10 }, { min: -30, max: -20 }] }, { min: 8, max: 15 }] }] };
-    //let test1: Operator<YangRange> = { operation: "OR", arguments: [{ operation: "OR", arguments: [{ min: -50, max: -40 }] }, { min: -30, max: -20 }, { min: 8, max: 15 }] };
-    //let test1: Operator<YangRange> = { operation: "AND", arguments: [{ operation: "OR", arguments: [{ min: -5, max: 10 }, { min: 17, max: 23 }] }] };
-
-    const number = data;
-
-    var expression = undefined;
-
-    if (isViewElementString(element)) {
-        expression = element.length;
-
-        errorMessageStart = rangeErrorStartString;
-        errorMessageMiddleMaxPart = rangeErrorinnerMaxTextString;
-        errorMessageMiddleMinPart = rangeErrorinnerMinTextString;
-        errorMessageEnd = rangeErrorEndTextString;
-
-    } else if (isViewElementNumber(element)) {
-        expression = element.range;
-
-        errorMessageStart = rangeErrorStartNumber;
-        errorMessageMiddleMaxPart = rangeErrorinnerMaxTextNumber;
-        errorMessageMiddleMinPart = rangeErrorinnerMinTextNumber;
-        errorMessageEnd = rangeErrorEndTextNumber;
-    }
-
-    if (expression) {
-        if (isYangOperator(expression)) {
-
-            const errorMessage = getRangeErrorMessages(expression, data);
-            return errorMessage;
-
-        } else
-            if (isYangRange(expression)) {
-
-                if (!isNaN(expression.min)) {
-                    if (number < expression.min) {
-                        return `${errorMessageStart} ${errorMessageMiddleMinPart} ${expression.min}${errorMessageEnd}`;
-                    }
-                }
-
-                if (!isNaN(expression.max)) {
-                    if (number > expression.max) {
-                        return `${errorMessageStart} ${errorMessageMiddleMaxPart} ${expression.max}${errorMessageEnd}`;
-                    }
-                }
-            }
-    }
-
-
-    return "";
-}
-
-function isYangRange(val: YangRange | Operator<YangRange>): val is YangRange {
-    return (val as YangRange).min !== undefined;
-}
-
-function isYangOperator(val: YangRange | Operator<YangRange>): val is Operator<YangRange> {
-    return (val as Operator<YangRange>).operation !== undefined;
-}
-
-function getRangeErrorMessagesRecursively(value: Operator<YangRange>, data: number): string[] {
-    let currentItteration: string[] = [];
-    console.log(value);
-
-    // itterate over all elements
-    for (let i = 0; i < value.arguments.length; i++) {
-        const element = value.arguments[i];
-
-        let min = undefined;
-        let max = undefined;
-
-        let isNumberCorrect = false;
-
-        if (isYangRange(element)) {
-
-            //check found min values
-            if (!isNaN(element.min)) {
-                if (data < element.min) {
-                    min = element.min;
-                } else {
-                    isNumberCorrect = true;
-                }
-            }
-
-            // check found max values
-            if (!isNaN(element.max)) {
-                if (data > element.max) {
-                    max = element.max;
-                } else {
-                    isNumberCorrect = true;
-                }
-            }
-
-            // construct error messages
-            if (min != undefined) {
-                currentItteration.push(`${value.operation.toLocaleLowerCase()} ${errorMessageMiddleMinPart} ${min}`);
-            } else if (max != undefined) {
-                currentItteration.push(`${value.operation.toLocaleLowerCase()} ${errorMessageMiddleMaxPart} ${max}`);
-
-            }
-
-        } else if (isYangOperator(element)) {
-
-            //get errormessages from expression
-            const result = getRangeErrorMessagesRecursively(element, data);
-            if (result.length === 0) {
-                isNumberCorrect = true;
-            }
-            currentItteration = currentItteration.concat(result);
-        }
-
-        // if its an OR operation, the number has been checked and min/max are empty (thus not violated)
-        // delete everything found (because at least one found is correct, therefore all are correct) and break from loop
-        if (min === undefined && max === undefined && isNumberCorrect && value.operation === "OR") {
-
-            currentItteration.splice(0, currentItteration.length);
-            break;
-        }
-    }
-
-    return currentItteration;
-}
-
-function getRangeErrorMessages(value: Operator<YangRange>, data: number): string {
-
-    const currentItteration = getRangeErrorMessagesRecursively(value, data);
-
-    // build complete error message from found parts
-    let errormessage = "";
-    if (currentItteration.length > 1) {
-
-        currentItteration.forEach((element, index) => {
-            if (index === 0) {
-                errormessage = createStartMessage(element);
-            } else if (index === currentItteration.length - 1) {
-                errormessage += ` ${element}${errorMessageEnd}`;
-            } else {
-                errormessage += `, ${element}`
-            }
-        });
-    } else if (currentItteration.length == 1) {
-        errormessage = `${createStartMessage(currentItteration[0])}${errorMessageEnd}`;
-    }
-
-    return errormessage;
-}
-
-function createStartMessage(element: string) {
-
-    //remove leading or or and from text
-    if (element.startsWith("and"))
-        element = element.replace("and", "");
-    else if (element.startsWith("or"))
-        element = element.replace("or", "");
-
-    return `${errorMessageStart} ${element}`;
-}
-
-export const checkPattern = (expression: RegExp | Operator<RegExp> | undefined, data: string): validated => {
-
-    if (expression) {
-        if (isRegExp(expression)) {
-            const isValid = expression.test(data);
-            if (!isValid)
-                return { isValid: isValid, error: "The input is in a wrong format." };
-
-        } else if (isRegExpOperator(expression)) {
-            const result = isPatternValid(expression, data);
-
-            if (!result) {
-                return { isValid: false, error: "The input is in a wrong format." };
-            }
-        }
-    }
-
-    return { isValid: true }
-}
-
-function getRegexRecursively(value: Operator<RegExp>, data: string): boolean[] {
-    let currentItteration: boolean[] = [];
-    for (let i = 0; i < value.arguments.length; i++) {
-        const element = value.arguments[i];
-        if (isRegExp(element)) {
-            // if regex is found, add it to list
-            currentItteration.push(element.test(data))
-        } else if (isRegExpOperator(element)) {
-            //if RegexExpression is found, try to get regex from it
-            currentItteration = currentItteration.concat(getRegexRecursively(element, data));
-        }
-    }
-
-    if (value.operation === "OR") {
-        // if one is true, all are true, all found items can be discarded
-        let result = currentItteration.find(element => element);
-        if (result) {
-            return [];
-        }
-    }
-    return currentItteration;
-}
-
-function isPatternValid(value: Operator<RegExp>, data: string): boolean {
-
-
-    // get all regex
-    const result = getRegexRecursively(value, data);
-    console.log(value);
-
-
-    if (value.operation === "AND") {
-        // if AND operation is executed...
-        // no element can be false
-        const check = result.find(element => element !== true);
-        if (check)
-            return false;
-        else
-            return true;
-    } else {
-        // if OR operation is executed...
-        // ... just one element must be true
-        const check = result.find(element => element === true);
-        if (check)
-            return true;
-        else
-            return false;
-
-    }
-}
-
-function isRegExp(val: RegExp | Operator<RegExp>): val is RegExp {
-    return (val as RegExp).source !== undefined;
-}
-
-function isRegExpOperator(val: RegExp | Operator<RegExp>): val is Operator<RegExp> {
-    return (val as Operator<RegExp>).operation !== undefined;
-}
\ No newline at end of file
index 1af699a..9cbd916 100644 (file)
@@ -19,9 +19,9 @@
 import { combineActionHandler } from '../../../../framework/src/flux/middleware';
 
 import { IConnectedNetworkElementsState, connectedNetworkElementsActionHandler } from './connectedNetworkElementsHandler';
-import { IDeviceDescriptionState, deviceDescriptionHandler } from "./deviceDescriptionHandler";
-import { IViewDescriptionState, viewDescriptionHandler } from "./viewDescriptionHandler";
-import { IValueSelectorState, valueSelectorHandler } from "./valueSelectorHandler";
+import { IDeviceDescriptionState, deviceDescriptionHandler } from './deviceDescriptionHandler';
+import { IViewDescriptionState, viewDescriptionHandler } from './viewDescriptionHandler';
+import { IValueSelectorState, valueSelectorHandler } from './valueSelectorHandler';
 
 interface IConfigurationAppStoreState {
   connectedNetworkElements: IConnectedNetworkElementsState; // used for ne selection
@@ -32,7 +32,7 @@ interface IConfigurationAppStoreState {
 
 declare module '../../../../framework/src/store/applicationStore' {
   interface IApplicationStoreState {
-    configuration: IConfigurationAppStoreState,
+    configuration: IConfigurationAppStoreState;
   }
 }
 
index 8ca8fdf..d2863dd 100644 (file)
@@ -25,8 +25,8 @@ import { restService } from '../services/restServices';
 
 export interface IConnectedNetworkElementsState extends IExternalTableState<NetworkElementConnection> { }
 
-// create eleactic search material data fetch handler
-const connectedNetworkElementsSearchHandler = createSearchDataHandler<NetworkElementConnection>('network-element-connection', false, { status: "Connected" });
+// create elastic search material data fetch handler
+const connectedNetworkElementsSearchHandler = createSearchDataHandler<NetworkElementConnection>('network-element-connection', false, { status: 'Connected' });
 
 export const {
   actionHandler: connectedNetworkElementsActionHandler,
@@ -41,5 +41,5 @@ export const {
     const neUrl = restService.getNetworkElementUri(ne.id);
     const policy = getAccessPolicyByUrl(neUrl);
     return !(policy.GET && policy.POST);
-  }
+  },
 );
index 408399d..cd01b09 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
-import { Module } from "../models/yang";
-import { ViewSpecification } from "../models/uiModels";
-import { IActionHandler } from "../../../../framework/src/flux/action";
-import { UpdateDeviceDescription } from "../actions/deviceActions";
+import { Module } from '../models/yang';
+import { ViewSpecification } from '../models/uiModels';
+import { IActionHandler } from '../../../../framework/src/flux/action';
+import { UpdateDeviceDescription } from '../actions/deviceActions';
 
 export interface IDeviceDescriptionState {
-  nodeId: string,
+  nodeId: string;
   modules: {
-    [name: string]: Module
-  },
-  views: ViewSpecification[],
+    [name: string]: Module;
+  };
+  views: ViewSpecification[];
 }
 
 const deviceDescriptionStateInit: IDeviceDescriptionState = {
-  nodeId: "",
+  nodeId: '',
   modules: {},
-  views: []
+  views: [],
 };
 
 export const deviceDescriptionHandler: IActionHandler<IDeviceDescriptionState> = (state = deviceDescriptionStateInit, action) => {
@@ -41,7 +41,7 @@ export const deviceDescriptionHandler: IActionHandler<IDeviceDescriptionState> =
       ...state,
       nodeId: action.nodeId,
       modules: action.modules,
-      views: action.views
+      views: action.views,
     };
   }
   return state;
index 5b2d55e..70d5eb2 100644 (file)
@@ -16,9 +16,9 @@
  * ============LICENSE_END==========================================================================
  */
 
-import { IActionHandler } from "../../../../framework/src/flux/action";
-import { ViewSpecification } from "../models/uiModels";
-import { EnableValueSelector, SetSelectedValue, UpdateDeviceDescription, SetCollectingSelectionData, UpdatViewDescription, UpdatOutputData } from "../actions/deviceActions";
+import { IActionHandler } from '../../../../framework/src/flux/action';
+import { ViewSpecification } from '../models/uiModels';
+import { EnableValueSelector, SetSelectedValue, UpdateDeviceDescription, SetCollectingSelectionData, UpdateViewDescription, UpdateOutputData } from '../actions/deviceActions';
 
 export interface IValueSelectorState {
   collectingData: boolean;
@@ -28,13 +28,13 @@ export interface IValueSelectorState {
   onValueSelected: (value: any) => void;
 }
 
-const nc = (val: React.SyntheticEvent) => { };
+const dummyFunc = () => { };
 const valueSelectorStateInit: IValueSelectorState = {
   collectingData: false,
   keyProperty: undefined,
   listSpecification: null,
   listData: [],
-  onValueSelected: nc,
+  onValueSelected: dummyFunc,
 };
 
 export const valueSelectorHandler: IActionHandler<IValueSelectorState> = (state = valueSelectorStateInit, action) => {
@@ -53,22 +53,24 @@ export const valueSelectorHandler: IActionHandler<IValueSelectorState> = (state
       listData: action.listData,
     };
   } else if (action instanceof SetSelectedValue) {
-    state.keyProperty && state.onValueSelected(action.value[state.keyProperty]);
+    if (state.keyProperty) {
+      state.onValueSelected(action.value[state.keyProperty]);
+    }
     state = {
       ...state,
       collectingData: false,
       keyProperty: undefined,
       listSpecification: null,
-      onValueSelected: nc,
+      onValueSelected: dummyFunc,
       listData: [],
     };
-  } else if (action instanceof UpdateDeviceDescription || action instanceof UpdatViewDescription || action instanceof UpdatOutputData) {
+  } else if (action instanceof UpdateDeviceDescription || action instanceof UpdateViewDescription || action instanceof UpdateOutputData) {
     state = {
       ...state,
       collectingData: false,
       keyProperty: undefined,
       listSpecification: null,
-      onValueSelected: nc,
+      onValueSelected: dummyFunc,
       listData: [],
     };
   }
index ff85a97..39b47be 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
-import { IActionHandler } from "../../../../framework/src/flux/action";
+import { IActionHandler } from '../../../../framework/src/flux/action';
 
-import { UpdatViewDescription, UpdatOutputData } from "../actions/deviceActions";
-import { ViewSpecification } from "../models/uiModels";
+import { UpdateViewDescription, UpdateOutputData } from '../actions/deviceActions';
+import { ViewSpecification } from '../models/uiModels';
 
 export enum DisplayModeType {
   doNotDisplay = 0,
   displayAsObject = 1,
   displayAsList = 2,
   displayAsRPC = 3,
-  displayAsMessage = 4
-};
+  displayAsMessage = 4,
+}
 
 export type DisplaySpecification =  {
   displayMode: DisplayModeType.doNotDisplay;
@@ -45,13 +45,13 @@ export type DisplaySpecification =  {
 } | {
   displayMode: DisplayModeType.displayAsMessage;
   renderMessage: string;
-}
+};
 
 export interface IViewDescriptionState {
   vPath: string | null;
   displaySpecification: DisplaySpecification;
-  viewData: any,
-  outputData?: any,
+  viewData: any;
+  outputData?: any;
 }
 
 const viewDescriptionStateInit: IViewDescriptionState = {
@@ -64,7 +64,7 @@ const viewDescriptionStateInit: IViewDescriptionState = {
 };
 
 export const viewDescriptionHandler: IActionHandler<IViewDescriptionState> = (state = viewDescriptionStateInit, action) => {
-  if (action instanceof UpdatViewDescription) {
+  if (action instanceof UpdateViewDescription) {
     state = {
       ...state,
       vPath: action.vPath,
@@ -72,7 +72,7 @@ export const viewDescriptionHandler: IActionHandler<IViewDescriptionState> = (st
       outputData: undefined,
       displaySpecification: action.displaySpecification,
     };
-  } else if (action instanceof UpdatOutputData) {
+  } else if (action instanceof UpdateOutputData) {
     state = {
       ...state,
       outputData: action.outputData,
index 88f7018..e1ef1ea 100644 (file)
@@ -24,7 +24,7 @@ export type NetworkElementConnection = {
   username?: string;
   password?: string;
   isRequired?: boolean;
-  status?: "connected" | "mounted" | "unmounted" | "connecting" | "disconnected" | "idle";
+  status?: 'connected' | 'mounted' | 'unmounted' | 'connecting' | 'disconnected' | 'idle';
   coreModelCapability?: string;
   deviceType?: string;
   nodeDetails?: {
@@ -33,5 +33,5 @@ export type NetworkElementConnection = {
       failureReason: string;
       capability: string;
     }[];
-  }
-}
+  };
+};
index 29484d8..7d9e63c 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
+import type { WhenAST } from '../yang/whenParser';
+
 export type ViewElementBase = {
-  "id": string;
-  "label": string;
-  "module": string;
-  "path": string;
-  "config": boolean;
-  "ifFeature"?: string;
-  "when"?: string;
-  "mandatory"?: boolean;
-  "description"?: string;
-  "isList"?: boolean;
-  "default"?: string;
-  "status"?: "current" | "deprecated" | "obsolete",
-  "reference"?: string, // https://tools.ietf.org/html/rfc7950#section-7.21.4
-}
+  'id': string;
+  'label': string;
+  'module': string;
+  'path': string;
+  'config': boolean;
+  'ifFeature'?: string;
+  'when'?: WhenAST;
+  'mandatory'?: boolean;
+  'description'?: string;
+  'isList'?: boolean;
+  'default'?: string;
+  'status'?: 'current' | 'deprecated' | 'obsolete';
+  'reference'?: string; // https://tools.ietf.org/html/rfc7950#section-7.21.4
+};
 
 // https://tools.ietf.org/html/rfc7950#section-9.8
 export type ViewElementBinary = ViewElementBase & {
-  "uiType": "binary";
-  "length"?: Expression<YangRange>;  // number of octets
-}
+  'uiType': 'binary';
+  'length'?: Expression<YangRange>;  // number of octets
+};
 
 // https://tools.ietf.org/html/rfc7950#section-9.7.4
 export type ViewElementBits = ViewElementBase & {
-  "uiType": "bits";
-  "flags": {
+  'uiType': 'bits';
+  'flags': {
     [name: string]: number | undefined;    // 0 - 4294967295
-  }
-}
+  };
+};
 
 // https://tools.ietf.org/html/rfc7950#section-9
 export type ViewElementString = ViewElementBase & {
-  "uiType": "string";
-  "pattern"?: Expression<RegExp>;
-  "length"?: Expression<YangRange>;
-  "invertMatch"?: true;
-}
+  'uiType': 'string';
+  'pattern'?: Expression<RegExp>;
+  'length'?: Expression<YangRange>;
+  'invertMatch'?: true;
+};
 
 // special case derived from 
 export type ViewElementDate = ViewElementBase & {
-  "uiType": "date";
-  "pattern"?: Expression<RegExp>;
-  "length"?: Expression<YangRange>;
-  "invertMatch"?: true;
-}
+  'uiType': 'date';
+  'pattern'?: Expression<RegExp>;
+  'length'?: Expression<YangRange>;
+  'invertMatch'?: true;
+};
 
 // https://tools.ietf.org/html/rfc7950#section-9.3
 export type ViewElementNumber = ViewElementBase & {
-  "uiType": "number";
-  "min": number;
-  "max": number;
-  "range"?: Expression<YangRange>;
-  "units"?: string;
-  "format"?: string;
-  "fDigits"?: number;
-}
+  'uiType': 'number';
+  'min': number;
+  'max': number;
+  'range'?: Expression<YangRange>;
+  'units'?: string;
+  'format'?: string;
+  'fDigits'?: number;
+};
 
 // https://tools.ietf.org/html/rfc7950#section-9.5
 export type ViewElementBoolean = ViewElementBase & {
-  "uiType": "boolean";
-  "trueValue"?: string;
-  "falseValue"?: string;
-}
+  'uiType': 'boolean';
+  'trueValue'?: string;
+  'falseValue'?: string;
+};
 
 // https://tools.ietf.org/html/rfc7950#section-9.6.4
 export type ViewElementSelection = ViewElementBase & {
-  "uiType": "selection";
-  "multiSelect"?: boolean
-  "options": {
-    "key": string;
-    "value": string;
-    "description"?: string,
-    "status"?: "current" | "deprecated" | "obsolete",
-    "reference"?: string,
+  'uiType': 'selection';
+  'multiSelect'?: boolean;
+  'options': {
+    'key': string;
+    'value': string;
+    'description'?: string;
+    'status'?: 'current' | 'deprecated' | 'obsolete';
+    'reference'?: string;
   }[];
-}
+};
 
 // is a list if isList is true ;-)
 export type ViewElementObject = ViewElementBase & {
-  "uiType": "object";
-  "isList"?: false;
-  "viewId": string;
-}
+  'uiType': 'object';
+  'isList'?: false;
+  'viewId': string;
+};
 
 // Hint: read only lists do not need a key
 export type ViewElementList = (ViewElementBase & {
-  "uiType": "object";
-  "isList": true;
-  "viewId": string;
-  "key"?: string;
+  'uiType': 'object';
+  'isList': true;
+  'viewId': string;
+  'key'?: string;
 });
 
 export type ViewElementReference = ViewElementBase & {
-  "uiType": "reference";
-  "referencePath": string;
-  "ref": (currentPath: string) => [ViewElement , string] | undefined;
-}
+  'uiType': 'reference';
+  'referencePath': string;
+  'ref': (currentPath: string) => [ViewElement, string] | undefined;
+};
 
 export type ViewElementUnion = ViewElementBase & {
-  "uiType": "union";
-  "elements": ViewElement[];
-}
+  'uiType': 'union';
+  'elements': ViewElement[];
+};
 
-export type ViewElementChoiseCase = { id: string, label: string, description?: string, elements: { [name: string]: ViewElement }  };
+export type ViewElementChoiceCase = { id: string; label: string; description?: string; elements: { [name: string]: ViewElement }  };
 
-export type ViewElementChoise = ViewElementBase & {
-  "uiType": "choise";
-  "cases": {
-    [name: string]: ViewElementChoiseCase;
-  }
-}
+export type ViewElementChoice = ViewElementBase & {
+  'uiType': 'choice';
+  'cases': {
+    [name: string]: ViewElementChoiceCase;
+  };
+};
 
 // https://tools.ietf.org/html/rfc7950#section-7.14.1
 export type ViewElementRpc = ViewElementBase & {
-  "uiType": "rpc";
-  "inputViewId"?: string;
-  "outputViewId"?: string;
-}
+  'uiType': 'rpc';
+  'inputViewId'?: string;
+  'outputViewId'?: string;
+};
 
 export type ViewElementEmpty = ViewElementBase & {
-  "uiType": "empty";
-}
+  'uiType': 'empty';
+};
 
 export type ViewElement =
   | ViewElementEmpty
@@ -152,88 +154,88 @@ export type ViewElement =
   | ViewElementSelection
   | ViewElementReference
   | ViewElementUnion
-  | ViewElementChoise
+  | ViewElementChoice
   | ViewElementRpc;
 
 export const isViewElementString = (viewElement: ViewElement): viewElement is ViewElementString => {
-  return viewElement && (viewElement.uiType === "string" || viewElement.uiType === "date");
-}
+  return viewElement && (viewElement.uiType === 'string' || viewElement.uiType === 'date');
+};
 
 export const isViewElementDate = (viewElement: ViewElement): viewElement is ViewElementDate => {
-  return viewElement && (viewElement.uiType === "date");
-}
+  return viewElement && (viewElement.uiType === 'date');
+};
 
 export const isViewElementNumber = (viewElement: ViewElement): viewElement is ViewElementNumber => {
-  return viewElement && viewElement.uiType === "number";
-}
+  return viewElement && viewElement.uiType === 'number';
+};
 
 export const isViewElementBoolean = (viewElement: ViewElement): viewElement is ViewElementBoolean => {
-  return viewElement && viewElement.uiType === "boolean";
-}
+  return viewElement && viewElement.uiType === 'boolean';
+};
 
 export const isViewElementObject = (viewElement: ViewElement): viewElement is ViewElementObject => {
-  return viewElement && viewElement.uiType === "object" && viewElement.isList === false;
-}
+  return viewElement && viewElement.uiType === 'object' && viewElement.isList === false;
+};
 
 export const isViewElementList = (viewElement: ViewElement): viewElement is ViewElementList => {
-  return viewElement && viewElement.uiType === "object" && viewElement.isList === true;
-}
+  return viewElement && viewElement.uiType === 'object' && viewElement.isList === true;
+};
 
 export const isViewElementObjectOrList = (viewElement: ViewElement): viewElement is ViewElementObject | ViewElementList => {
-  return viewElement && viewElement.uiType === "object";
-}
+  return viewElement && viewElement.uiType === 'object';
+};
 
 export const isViewElementSelection = (viewElement: ViewElement): viewElement is ViewElementSelection => {
-  return viewElement && viewElement.uiType === "selection";
-}
+  return viewElement && viewElement.uiType === 'selection';
+};
 
 export const isViewElementReference = (viewElement: ViewElement): viewElement is ViewElementReference => {
-  return viewElement && viewElement.uiType === "reference";
-}
+  return viewElement && viewElement.uiType === 'reference';
+};
 
 export const isViewElementUnion = (viewElement: ViewElement): viewElement is ViewElementUnion => {
-  return viewElement && viewElement.uiType === "union";
-}
+  return viewElement && viewElement.uiType === 'union';
+};
 
-export const isViewElementChoise = (viewElement: ViewElement): viewElement is ViewElementChoise => {
-  return viewElement && viewElement.uiType === "choise";
-}
+export const isViewElementChoice = (viewElement: ViewElement): viewElement is ViewElementChoice => {
+  return viewElement && viewElement.uiType === 'choice';
+};
 
 export const isViewElementRpc = (viewElement: ViewElement): viewElement is ViewElementRpc => {
-  return viewElement && viewElement.uiType === "rpc";
-}
+  return viewElement && viewElement.uiType === 'rpc';
+};
 
 export const isViewElementEmpty = (viewElement: ViewElement): viewElement is ViewElementRpc => {
-  return viewElement && viewElement.uiType === "empty";
-}
+  return viewElement && viewElement.uiType === 'empty';
+};
 
-export const ResolveFunction = Symbol("IsResolved");
+export const ResolveFunction = Symbol('IsResolved');
 
 export type ViewSpecification = {
-  "id": string;
-  "ns"?: string;
-  "name"?: string;
-  "title"?: string;
-  "parentView"?: string;
-  "language": string;
-  "ifFeature"?: string;
-  "when"?: string;
-  "uses"?: (string[]) & { [ResolveFunction]?: (parent: string) => void };
-  "elements": { [name: string]: ViewElement };
-  "config": boolean;
-  readonly "canEdit": boolean;
-}
+  id: string;
+  ns?: string;
+  name?: string;
+  title?: string;
+  parentView?: string;
+  language: string;
+  ifFeature?: string;
+  when?: WhenAST;
+  uses?: (string[]) & { [ResolveFunction]?: (parent: string) => void };
+  elements: { [name: string]: ViewElement };
+  config: boolean;
+  readonly canEdit: boolean;
+};
 
 export type YangRange = {
-  min: number,
-  max: number,
-}
+  min: number;
+  max: number;
+};
 
 export type Expression<T> =
   | T
   | Operator<T>;
 
 export type Operator<T> = {
-  operation: "AND" | "OR";
+  operation: 'AND' | 'OR';
   arguments: Expression<T>[];
-}
\ No newline at end of file
+};
\ No newline at end of file
index 79704ae..e4e59fb 100644 (file)
@@ -16,7 +16,7 @@
  * ============LICENSE_END==========================================================================
  */
 
-import { ViewElement, ViewSpecification } from "./uiModels";
+import { ViewElement, ViewSpecification } from './uiModels';
 
 export enum ModuleState {
   stable,
@@ -30,27 +30,27 @@ export type Token = {
   value: string;
   start: number;
   end: number;
-}
+};
 
 export type Statement = {
   key: string;
   arg?: string;
   sub?: Statement[];
-}
+};
 
 export type Identity = {
-  id: string,
-  label: string,
-  base?: string,
-  description?: string,
-  reference?: string,
-  children?: Identity[],
-  values?: Identity[],
-}
+  id: string;
+  label: string;
+  base?: string;
+  description?: string;
+  reference?: string;
+  children?: Identity[];
+  values?: Identity[];
+};
 
 export type Revision = {
-  description?: string,
-  reference?: string
+  description?: string;
+  reference?: string;
 };
 
 export type Module = {
@@ -68,4 +68,4 @@ export type Module = {
   views: { [view: string]: ViewSpecification };
   elements: { [view: string]: ViewElement };
   executionOrder?: number;
-}
\ No newline at end of file
+};
\ No newline at end of file
index e378791..7dd2d6a 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
-import * as React from "react";
+import React from 'react';
 import { withRouter, RouteComponentProps, Route, Switch, Redirect } from 'react-router-dom';
 
-import { faAdjust } from '@fortawesome/free-solid-svg-icons';  // select app icon
-
-import connect, { Connect, IDispatcher } from '../../../framework/src/flux/connect';
+import { connect, Connect, IDispatcher } from '../../../framework/src/flux/connect';
 import applicationManager from '../../../framework/src/services/applicationManager';
-import { IApplicationStoreState } from "../../../framework/src/store/applicationStore";
-import { configurationAppRootHandler } from "./handlers/configurationAppRootHandler";
-import { NetworkElementSelector } from "./views/networkElementSelector";
 
-import ConfigurationApplication from "./views/configurationApplication";
-import { updateNodeIdAsyncActionCreator, updateViewActionAsyncCreator } from "./actions/deviceActions";
-import { DisplayModeType } from "./handlers/viewDescriptionHandler";
-import { ViewSpecification } from "./models/uiModels";
+import { configurationAppRootHandler } from './handlers/configurationAppRootHandler';
+import { NetworkElementSelector } from './views/networkElementSelector';
+
+import ConfigurationApplication from './views/configurationApplication';
+import { updateNodeIdAsyncActionCreator, updateViewActionAsyncCreator } from './actions/deviceActions';
+import { DisplayModeType } from './handlers/viewDescriptionHandler';
+import { ViewSpecification } from './models/uiModels';
+
+const appIcon = require('./assets/icons/configurationAppIcon.svg');  // select app icon
 
 let currentNodeId: string | null | undefined = undefined;
 let currentVirtualPath: string | null | undefined = undefined;
 let lastUrl: string | undefined = undefined;
 
-const mapDisp = (dispatcher: IDispatcher) => ({
+const mapDispatch = (dispatcher: IDispatcher) => ({
   updateNodeId: (nodeId: string) => dispatcher.dispatch(updateNodeIdAsyncActionCreator(nodeId)),
   updateView: (vPath: string) => dispatcher.dispatch(updateViewActionAsyncCreator(vPath)),
 });
 
-const ConfigurationApplicationRouteAdapter = connect(undefined, mapDisp)((props: RouteComponentProps<{ nodeId?: string, 0: string }> & Connect<undefined, typeof mapDisp>) => {
+// eslint-disable-next-line @typescript-eslint/naming-convention
+const ConfigurationApplicationRouteAdapter = connect(undefined, mapDispatch)((props: RouteComponentProps<{ nodeId?: string; 0: string }> & Connect<undefined, typeof mapDispatch>) => {
   React.useEffect(() => {
     return () => {
       lastUrl = undefined;
       currentNodeId = undefined;
       currentVirtualPath = undefined;
-    }
+    };
   }, []);
   if (props.location.pathname !== lastUrl) {
-    // ensure the asynchronus update will only be called once per path
+    // ensure the asynchronous update will only be called once per path
     lastUrl = props.location.pathname;
     window.setTimeout(async () => {
 
       // check if the nodeId has changed
-      let dump = false;
+      let enableDump = false;
       if (currentNodeId !== props.match.params.nodeId) {
         currentNodeId = props.match.params.nodeId || undefined;
-        if (currentNodeId && currentNodeId.endsWith("|dump")) {
-          dump = true;
+        if (currentNodeId && currentNodeId.endsWith('|dump')) {
+          enableDump = true;
           currentNodeId = currentNodeId.replace(/\|dump$/i, '');
         }
         currentVirtualPath = null;
-        currentNodeId && (await props.updateNodeId(currentNodeId));
+        if (currentNodeId) {
+          await props.updateNodeId(currentNodeId);
+        }
       }
 
       if (currentVirtualPath !== props.match.params[0]) {
         currentVirtualPath = props.match.params[0];
-        if (currentVirtualPath && currentVirtualPath.endsWith("|dump")) {
-          dump = true;
+        if (currentVirtualPath && currentVirtualPath.endsWith('|dump')) {
+          enableDump = true;
           currentVirtualPath = currentVirtualPath.replace(/\|dump$/i, '');
         }
         await props.updateView(currentVirtualPath);
       }
 
-      if (dump) {
+      if (enableDump) {
         const device = props.state.configuration.deviceDescription;
         const ds = props.state.configuration.viewDescription.displaySpecification;
 
         const createDump = (view: ViewSpecification | null, level: number = 0) => {
-          if (view === null) return "Empty";
+          if (view === null) return 'Empty';
           const indention = Array(level * 4).fill(' ').join('');
           let result = '';
 
@@ -88,24 +91,24 @@ const ConfigurationApplicationRouteAdapter = connect(undefined, mapDisp)((props:
           // result += `${indention}  [${view.canEdit ? 'rw' : 'ro'}] ${view.ns}:${view.name} ${ds.displayMode === DisplayModeType.displayAsList ? '[LIST]' : ''}\r\n`;
           result += Object.keys(view.elements).reduce((acc, cur) => {
             const elm = view.elements[cur];
-            acc += `${indention}  [${elm.uiType === "rpc" ? "x" : elm.config ? 'rw' : 'ro'}:${elm.id}] (${elm.module}:${elm.label}) {${elm.uiType}} ${elm.uiType === "object" && elm.isList ? `as LIST with KEY [${elm.key}]` : ""}\r\n`;
-            // acc += `${indention}    +${elm.mandatory ? "mandetory" : "none"} - ${elm.path} \r\n`;
+            acc += `${indention}  [${elm.uiType === 'rpc' ? 'x' : elm.config ? 'rw' : 'ro'}:${elm.id}] (${elm.module}:${elm.label}) {${elm.uiType}} ${elm.uiType === 'object' && elm.isList ? `as LIST with KEY [${elm.key}]` : ''}\r\n`;
+            // acc += `${indention}    +${elm.mandatory ? "mandatory" : "none"} - ${elm.path} \r\n`;
             
             switch (elm.uiType) {
-              case "object":
+              case 'object':
                 acc += createDump(device.views[(elm as any).viewId], level + 1);
                 break;
               default:
             }
             return acc;
-          }, "");
+          }, '');
           return `${result}`;
-        }
+        };
 
         const dump = createDump(ds.displayMode === DisplayModeType.displayAsObject || ds.displayMode === DisplayModeType.displayAsList ? ds.viewSpecification : null, 0);
-        var element = document.createElement('a');
+        const element = document.createElement('a');
         element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(dump));
-        element.setAttribute('download', currentNodeId + ".txt");
+        element.setAttribute('download', currentNodeId + '.txt');
 
         element.style.display = 'none';
         document.body.appendChild(element);
@@ -133,10 +136,10 @@ const App = withRouter((props: RouteComponentProps) => (
 
 export function register() {
   applicationManager.registerApplication({
-    name: "configuration",
-    icon: faAdjust,
+    name: 'configuration',
+    icon: appIcon,
     rootComponent: App,
     rootActionHandler: configurationAppRootHandler,
-    menuEntry: "Configuration"
+    menuEntry: 'Configuration',
   });
 }
index 02060ef..07e2635 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
-import { requestRest, requestRestExt } from "../../../../framework/src/services/restService";
-import { convertPropertyNames, replaceHyphen } from "../../../../framework/src/utilities/yangHelper";
+import { requestRest, requestRestExt } from '../../../../framework/src/services/restService';
+import { convertPropertyNames, replaceHyphen } from '../../../../framework/src/utilities/yangHelper';
 
-import { NetworkElementConnection } from "../models/networkElementConnection";
+import { NetworkElementConnection } from '../models/networkElementConnection';
 
 type ImportOnlyResponse = {
-  "ietf-yang-library:yang-library": {
-    "module-set": {
-      "import-only-module": {
-        "name": string,
-        "revision": string,
-      }[],
-    }[],
-  },
-}
+  'ietf-yang-library:yang-library': {
+    'module-set': {
+      'import-only-module': {
+        'name': string;
+        'revision': string;
+      }[];
+    }[];
+  };
+};
 
 
 type CapabilityResponse = {
-  "network-topology:node": {
-    "node-id": string,
-    "netconf-node-topology:available-capabilities": {
-      "available-capability": {
-        "capability-origin": string,
-        "capability": string,
-      }[]
-    },
-    "netconf-node-topology:unavailable-capabilities": {
-      "unavailable-capability": {
-        "capability": string,
-        "failure-reason": string,
-      }[]
-    }
-  }[]
-}
+  'network-topology:node': {
+    'node-id': string;
+    'netconf-node-topology:available-capabilities': {
+      'available-capability': {
+        'capability-origin': string;
+        'capability': string;
+      }[];
+    };
+    'netconf-node-topology:unavailable-capabilities': {
+      'unavailable-capability': {
+        'capability': string;
+        'failure-reason': string;
+      }[];
+    };
+  }[];
+};
 
 type CapabilityAnswer = {
   availableCapabilities: {
-    capabilityOrigin: string,
-    capability: string,
-    version: string,
-  }[] | null,
+    capabilityOrigin: string;
+    capability: string;
+    version: string;
+  }[] | null;
   unavailableCapabilities: {
-    failureReason: string,
-    capability: string,
-    version: string,
-  }[] | null,
+    failureReason: string;
+    capability: string;
+    version: string;
+  }[] | null;
   importOnlyModules: {
-    name: string,
-    revision: string,
-  }[] | null
-}
+    name: string;
+    revision: string;
+  }[] | null;
+};
 
 const capParser = /^\(.*\?revision=(\d{4}-\d{2}-\d{2})\)(\S+)$/i;
 
 class RestService {
   public getNetworkElementUri = (nodeId: string) => '/rests/data/network-topology:network-topology/topology=topology-netconf/node=' + nodeId;
 
-  public async getImportOnlyModules(nodeId: string): Promise<{ name: string, revision: string }[]> {
+  public async getImportOnlyModules(nodeId: string): Promise<{ name: string; revision: string }[]> {
     const path = `${this.getNetworkElementUri(nodeId)}/yang-ext:mount/ietf-yang-library:yang-library?content=nonconfig&fields=module-set(import-only-module(name;revision))`;
-    const importOnlyResult = await requestRest<ImportOnlyResponse>(path, { method: "GET" });
+    const importOnlyResult = await requestRest<ImportOnlyResponse>(path, { method: 'GET' });
     const importOnlyModules = importOnlyResult
-      ? importOnlyResult["ietf-yang-library:yang-library"]["module-set"][0]["import-only-module"]
+      ? importOnlyResult['ietf-yang-library:yang-library']['module-set'][0]['import-only-module']
       : [];
     return importOnlyModules;
   }
 
   public async getCapabilitiesByMountId(nodeId: string): Promise<CapabilityAnswer> {
     const path = this.getNetworkElementUri(nodeId);
-    const capabilitiesResult = await requestRest<CapabilityResponse>(path, { method: "GET" });
-    const availableCapabilities = capabilitiesResult && capabilitiesResult["network-topology:node"] && capabilitiesResult["network-topology:node"].length > 0 &&
-      (capabilitiesResult["network-topology:node"][0]["netconf-node-topology:available-capabilities"] &&
-        capabilitiesResult["network-topology:node"][0]["netconf-node-topology:available-capabilities"]["available-capability"] &&
-        capabilitiesResult["network-topology:node"][0]["netconf-node-topology:available-capabilities"]["available-capability"].map<any>(obj => convertPropertyNames(obj, replaceHyphen)) || [])
+    const capabilitiesResult = await requestRest<CapabilityResponse>(path, { method: 'GET' });
+    const availableCapabilities = capabilitiesResult && capabilitiesResult['network-topology:node'] && capabilitiesResult['network-topology:node'].length > 0 &&
+      (capabilitiesResult['network-topology:node'][0]['netconf-node-topology:available-capabilities'] &&
+        capabilitiesResult['network-topology:node'][0]['netconf-node-topology:available-capabilities']['available-capability'] &&
+        capabilitiesResult['network-topology:node'][0]['netconf-node-topology:available-capabilities']['available-capability'].map<any>(obj => convertPropertyNames(obj, replaceHyphen)) || [])
         .map(cap => {
           const capMatch = cap && capParser.exec(cap.capability);
           return capMatch ? {
@@ -98,20 +98,20 @@ class RestService {
           } : null ;
         }).filter((cap) => cap != null) || [] as any;
 
-    const unavailableCapabilities = capabilitiesResult && capabilitiesResult["network-topology:node"] && capabilitiesResult["network-topology:node"].length > 0 &&
-      (capabilitiesResult["network-topology:node"][0]["netconf-node-topology:unavailable-capabilities"] &&
-      capabilitiesResult["network-topology:node"][0]["netconf-node-topology:unavailable-capabilities"]["unavailable-capability"] &&
-      capabilitiesResult["network-topology:node"][0]["netconf-node-topology:unavailable-capabilities"]["unavailable-capability"].map<any>(obj => convertPropertyNames(obj, replaceHyphen)) || [])
-      .map(cap => {
-        const capMatch = cap && capParser.exec(cap.capability);
-        return capMatch ? {
-          failureReason: cap.failureReason,
-          capability: capMatch && capMatch[2] || '',
-          version: capMatch && capMatch[1] || '',
-        } : null ;
-    }).filter((cap) => cap != null) || [] as any;
-
-    const importOnlyModules = availableCapabilities && availableCapabilities.findIndex((ac: {capability: string }) => ac.capability && ac.capability.toLowerCase() === "ietf-yang-library") > -1
+    const unavailableCapabilities = capabilitiesResult && capabilitiesResult['network-topology:node'] && capabilitiesResult['network-topology:node'].length > 0 &&
+      (capabilitiesResult['network-topology:node'][0]['netconf-node-topology:unavailable-capabilities'] &&
+      capabilitiesResult['network-topology:node'][0]['netconf-node-topology:unavailable-capabilities']['unavailable-capability'] &&
+      capabilitiesResult['network-topology:node'][0]['netconf-node-topology:unavailable-capabilities']['unavailable-capability'].map<any>(obj => convertPropertyNames(obj, replaceHyphen)) || [])
+        .map(cap => {
+          const capMatch = cap && capParser.exec(cap.capability);
+          return capMatch ? {
+            failureReason: cap.failureReason,
+            capability: capMatch && capMatch[2] || '',
+            version: capMatch && capMatch[1] || '',
+          } : null ;
+        }).filter((cap) => cap != null) || [] as any;
+
+    const importOnlyModules = availableCapabilities && availableCapabilities.findIndex((ac: { capability: string }) => ac.capability && ac.capability.toLowerCase() === 'ietf-yang-library') > -1
       ? await this.getImportOnlyModules(nodeId)
       : null;
 
@@ -123,11 +123,11 @@ class RestService {
     // const connectedNetworkElement = await requestRest<NetworkElementConnection>(path, { method: "GET" });
     // return connectedNetworkElement || null;
 
-    const path = "/rests/operations/data-provider:read-network-element-connection-list";
-    const body = { "data-provider:input": { "filter": [{ "property": "node-id", "filtervalue": nodeId }], "sortorder": [], "pagination": { "size": 1, "page": 1 } } };
-    const networkElementResult = await requestRest<{ "data-provider:output": { data: NetworkElementConnection[] } }>(path, { method: "POST", body: JSON.stringify(body) });
-    return networkElementResult && networkElementResult["data-provider:output"] && networkElementResult["data-provider:output"].data &&
-      networkElementResult["data-provider:output"].data.map(obj => convertPropertyNames(obj, replaceHyphen))[0] || null;
+    const path = '/rests/operations/data-provider:read-network-element-connection-list';
+    const body = { 'data-provider:input': { 'filter': [{ 'property': 'node-id', 'filtervalue': nodeId }], 'sortorder': [], 'pagination': { 'size': 1, 'page': 1 } } };
+    const networkElementResult = await requestRest<{ 'data-provider:output': { data: NetworkElementConnection[] } }>(path, { method: 'POST', body: JSON.stringify(body) });
+    return networkElementResult && networkElementResult['data-provider:output'] && networkElementResult['data-provider:output'].data &&
+      networkElementResult['data-provider:output'].data.map(obj => convertPropertyNames(obj, replaceHyphen))[0] || null;
   }
 
   /** Reads the config data by restconf path.
@@ -135,7 +135,7 @@ class RestService {
   * @returns The data.
   */
   public getConfigData(path: string) {
-    return requestRestExt<{ [key: string]: any }>(path, { method: "GET" });
+    return requestRestExt<{ [key: string]: any }>(path, { method: 'GET' });
   }
 
   /** Updates or creates the config data by restconf path using data.
@@ -144,11 +144,11 @@ class RestService {
    * @returns The written data.
    */
   public setConfigData(path: string, data: any) {
-    return requestRestExt<{ [key: string]: any }>(path, { method: "PUT", body: JSON.stringify(data) });
+    return requestRestExt<{ [key: string]: any }>(path, { method: 'PUT', body: JSON.stringify(data) });
   }
 
   public executeRpc(path: string, data: any) {
-    return requestRestExt<{ [key: string]: any }>(path, { method: "POST", body: JSON.stringify(data) });
+    return requestRestExt<{ [key: string]: any }>(path, { method: 'POST', body: JSON.stringify(data) });
   }
 
   /** Removes the element by restconf path.
@@ -156,7 +156,7 @@ class RestService {
   * @returns The restconf result.
   */
   public removeConfigElement(path: string) {
-    return requestRestExt<{ [key: string]: any }>(path, { method: "DELETE" });
+    return requestRestExt<{ [key: string]: any }>(path, { method: 'DELETE' });
   }
 }
 
index b81a92c..bbd051a 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
-type YangInfo = [string, (string | null | undefined)];
+const cache: { [path: string]: string } = { };
+const getCapability = async (capability: string, nodeId: string, version?: string) => {
+  const url = `/yang-schema/${capability}${version ? `/${version}` : ''}?node=${nodeId}`;
 
-const cache: { [path: string]: string } = {
+  const cacheHit = cache[url];
+  if (cacheHit) return cacheHit;
 
-};
-
-class YangService {
-
-  public async getCapability(capability: string, nodeId: string, version?: string) {
-    const url = `/yang-schema/${capability}${version ? `/${version}` : ""}?node=${nodeId}`;
-
-    const cacheHit = cache[url];
-    if (cacheHit) return cacheHit;
-
-    const res = await fetch(url);
-    const yangFile = res.ok && (await res.text());
-    if (yangFile !== false && yangFile !== null) {
-      cache[url] = yangFile;
-    }
-    return yangFile;
+  const res = await fetch(url);
+  const yangFile = res.ok && (await res.text());
+  if (yangFile !== false && yangFile !== null) {
+    cache[url] = yangFile;
   }
-}
+  return yangFile;
+};
 
-export const yangService = new YangService();
+export const yangService = {
+  getCapability,
+};
 export default yangService;
\ No newline at end of file
diff --git a/sdnr/wt/odlux/apps/configurationApp/src/utilities/verifyer.ts b/sdnr/wt/odlux/apps/configurationApp/src/utilities/verifyer.ts
new file mode 100644 (file)
index 0000000..9dd1203
--- /dev/null
@@ -0,0 +1,261 @@
+/**
+ * ============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 { YangRange, Operator, ViewElementNumber, ViewElementString, isViewElementNumber, isViewElementString } from '../models/uiModels';
+
+export type validated = { isValid: boolean; error?: string };
+
+export type validatedRange = { isValid: boolean; error?: string };
+
+const rangeErrorStartNumber = 'The entered number must be';
+const rangeErrorInnerMinTextNumber = 'greater or equals than';
+const rangeErrorInnerMaxTextNumber = 'less or equals than';
+const rangeErrorEndTextNumber = '.';
+
+const rangeErrorStartString = 'The entered text must have';
+const rangeErrorInnerMinTextString = 'no more than';
+const rangeErrorInnerMaxTextString = 'less than';
+const rangeErrorEndTextString = ' characters.';
+
+let errorMessageStart = '';
+let errorMessageMiddleMinPart = '';
+let errorMessageMiddleMaxPart = '';
+let errorMessageEnd = '';
+
+const isYangRange = (val: YangRange | Operator<YangRange>): val is YangRange => (val as YangRange).min !== undefined;
+  
+const isYangOperator = (val: YangRange | Operator<YangRange>): val is Operator<YangRange> => (val as Operator<YangRange>).operation !== undefined;
+
+const isRegExp = (val: RegExp | Operator<RegExp>): val is RegExp => (val as RegExp).source !== undefined;
+
+const isRegExpOperator = (val: RegExp | Operator<RegExp>): val is Operator<RegExp> => (val as Operator<RegExp>).operation !== undefined;
+
+const getRangeErrorMessagesRecursively = (value: Operator<YangRange>, data: number): string[] => {
+  let currentIteration: string[] = [];
+  
+  // iterate over all elements
+  for (let i = 0; i < value.arguments.length; i++) {
+    const element = value.arguments[i];
+  
+    let min = undefined;
+    let max = undefined;
+  
+    let isNumberCorrect = false;
+  
+    if (isYangRange(element)) {
+  
+      //check found min values
+      if (!isNaN(element.min)) {
+        if (data < element.min) {
+          min = element.min;
+        } else {
+          isNumberCorrect = true;
+        }
+      }
+  
+      // check found max values
+      if (!isNaN(element.max)) {
+        if (data > element.max) {
+          max = element.max;
+        } else {
+          isNumberCorrect = true;
+        }
+      }
+  
+      // construct error messages
+      if (min != undefined) {
+        currentIteration.push(`${value.operation.toLocaleLowerCase()} ${errorMessageMiddleMinPart} ${min}`);
+      } else if (max != undefined) {
+        currentIteration.push(`${value.operation.toLocaleLowerCase()} ${errorMessageMiddleMaxPart} ${max}`);
+  
+      }
+  
+    } else if (isYangOperator(element)) {
+  
+      //get error_message from expression
+      const result = getRangeErrorMessagesRecursively(element, data);
+      if (result.length === 0) {
+        isNumberCorrect = true;
+      }
+      currentIteration = currentIteration.concat(result);
+    }
+  
+    // if its an OR operation, the number has been checked and min/max are empty (thus not violated)
+    // delete everything found (because at least one found is correct, therefore all are correct) and break from loop
+    if (min === undefined && max === undefined && isNumberCorrect && value.operation === 'OR') {
+  
+      currentIteration.splice(0, currentIteration.length);
+      break;
+    }
+  }
+  
+  return currentIteration;
+};
+
+const createStartMessage = (element: string) => {
+  //remove leading or or and from text
+  if (element.startsWith('and')) {
+    element = element.replace('and', '');
+  } else if (element.startsWith('or')) {
+    element = element.replace('or', '');
+  }
+  return `${errorMessageStart} ${element}`;
+};
+  
+const getRangeErrorMessages = (value: Operator<YangRange>, data: number): string => {
+
+  const currentIteration = getRangeErrorMessagesRecursively(value, data);
+  
+  // build complete error message from found parts
+  let errorMessage = '';
+  if (currentIteration.length > 1) {
+  
+    currentIteration.forEach((element, index) => {
+      if (index === 0) {
+        errorMessage = createStartMessage(element);
+      } else if (index === currentIteration.length - 1) {
+        errorMessage += ` ${element}${errorMessageEnd}`;
+      } else {
+        errorMessage += `, ${element}`;
+      }
+    });
+  } else if (currentIteration.length == 1) {
+    errorMessage = `${createStartMessage(currentIteration[0])}${errorMessageEnd}`;
+  }
+  
+  return errorMessage;
+};
+
+export const checkRange = (element: ViewElementNumber | ViewElementString, data: number): string => {
+  const number = data;
+
+  let expression = undefined;
+
+  if (isViewElementString(element)) {
+    expression = element.length;
+
+    errorMessageStart = rangeErrorStartString;
+    errorMessageMiddleMaxPart = rangeErrorInnerMaxTextString;
+    errorMessageMiddleMinPart = rangeErrorInnerMinTextString;
+    errorMessageEnd = rangeErrorEndTextString;
+
+  } else if (isViewElementNumber(element)) {
+    expression = element.range;
+
+    errorMessageStart = rangeErrorStartNumber;
+    errorMessageMiddleMaxPart = rangeErrorInnerMaxTextNumber;
+    errorMessageMiddleMinPart = rangeErrorInnerMinTextNumber;
+    errorMessageEnd = rangeErrorEndTextNumber;
+  }
+
+  if (expression) {
+    if (isYangOperator(expression)) {
+
+      const errorMessage = getRangeErrorMessages(expression, data);
+      return errorMessage;
+
+    } else
+    if (isYangRange(expression)) {
+
+      if (!isNaN(expression.min)) {
+        if (number < expression.min) {
+          return `${errorMessageStart} ${errorMessageMiddleMinPart} ${expression.min}${errorMessageEnd}`;
+        }
+      }
+
+      if (!isNaN(expression.max)) {
+        if (number > expression.max) {
+          return `${errorMessageStart} ${errorMessageMiddleMaxPart} ${expression.max}${errorMessageEnd}`;
+        }
+      }
+    }
+  }
+
+  return '';
+};
+
+const getRegexRecursively = (value: Operator<RegExp>, data: string): boolean[] => {
+  let currentItteration: boolean[] = [];
+  for (let i = 0; i < value.arguments.length; i++) {
+    const element = value.arguments[i];
+    if (isRegExp(element)) {
+      // if regex is found, add it to list
+      currentItteration.push(element.test(data));
+    } else if (isRegExpOperator(element)) {
+      //if RegexExpression is found, try to get regex from it
+      currentItteration = currentItteration.concat(getRegexRecursively(element, data));
+    }
+  }
+  
+  if (value.operation === 'OR') {
+    // if one is true, all are true, all found items can be discarded
+    let result = currentItteration.find(element => element);
+    if (result) {
+      return [];
+    }
+  }
+  return currentItteration;
+};
+  
+const isPatternValid = (value: Operator<RegExp>, data: string): boolean => {
+  // get all regex
+  const result = getRegexRecursively(value, data);
+  
+  if (value.operation === 'AND') {
+    // if AND operation is executed...
+    // no element can be false
+    const check = result.find(element => element !== true);
+    if (check)
+      return false;
+    else
+      return true;
+  } else {
+    // if OR operation is executed...
+    // ... just one element must be true
+    const check = result.find(element => element === true);
+    if (check)
+      return true;
+    else
+      return false;
+  
+  }
+};
+
+export const checkPattern = (expression: RegExp | Operator<RegExp> | undefined, data: string): validated => {
+
+  if (expression) {
+    if (isRegExp(expression)) {
+      const isValid = expression.test(data);
+      if (!isValid)
+        return { isValid: isValid, error: 'The input is in a wrong format.' };
+
+    } else if (isRegExpOperator(expression)) {
+      const result = isPatternValid(expression, data);
+
+      if (!result) {
+        return { isValid: false, error: 'The input is in a wrong format.' };
+      }
+    }
+  }
+
+  return { isValid: true };
+};
+
+
+
+
diff --git a/sdnr/wt/odlux/apps/configurationApp/src/utilities/viewEngineHelper.ts b/sdnr/wt/odlux/apps/configurationApp/src/utilities/viewEngineHelper.ts
new file mode 100644 (file)
index 0000000..ad34c83
--- /dev/null
@@ -0,0 +1,324 @@
+import { storeService } from '../../../../framework/src/services/storeService';
+import { WhenAST, WhenTokenType } from '../yang/whenParser';
+
+import {
+  ViewSpecification,
+  ViewElement,
+  isViewElementReference,
+  isViewElementList,
+  isViewElementObjectOrList,
+  isViewElementRpc,
+  isViewElementChoice,
+  ViewElementChoiceCase,
+} from '../models/uiModels';
+
+import { Module } from '../models/yang';
+
+import { restService } from '../services/restServices';
+
+export type HttpResult = {
+  status: number;
+  message?: string | undefined;
+  data: {
+    [key: string]: any;
+  } | null | undefined;
+};
+
+export const checkResponseCode = (restResult: HttpResult) =>{
+  //403 gets handled by the framework from now on
+  return restResult.status !== 403 && ( restResult.status < 200 || restResult.status > 299);
+};
+
+export const resolveVPath = (current: string, vPath: string): string => {
+  if (vPath.startsWith('/')) {
+    return vPath;
+  }
+  const parts = current.split('/');
+  const vPathParts = vPath.split('/');
+  for (const part of vPathParts) {
+    if (part === '.') {
+      continue;
+    } else if (part === '..') {
+      parts.pop();
+    } else {
+      parts.push(part);
+    }
+  }
+  return parts.join('/');
+};
+
+export const splitVPath = (vPath: string, vPathParser : RegExp): [string, string?][] => {
+  const pathParts: [string, string?][] = [];
+  let partMatch: RegExpExecArray | null;
+  if (vPath) do {
+    partMatch = vPathParser.exec(vPath);
+    if (partMatch) {
+      pathParts.push([partMatch[1], partMatch[2] || undefined]);
+    }
+  } while (partMatch);
+  return pathParts;
+};
+
+const derivedFrom = (vPath: string, when: WhenAST, viewData: any, includeSelf = false) => {
+  if (when.args?.length !== 2) {
+    throw new Error('derived-from or derived-from-or-self requires 2 arguments.');
+  }
+  const [arg1, arg2] = when.args;
+  if (arg1.type !== WhenTokenType.IDENTIFIER || arg2.type !== WhenTokenType.STRING) {
+    throw new Error('derived-from or derived-from-or-self requires first argument IDENTIFIER and second argument STRING.');
+  }
+
+  if (!storeService.applicationStore) {
+    throw new Error('storeService.applicationStore is not defined.');
+  }
+
+  const pathParts = splitVPath(arg1.value as string || '', /(?:(?:([^\/\:]+):)?([^\/]+))/g); 
+  const referenceValueParts = /(?:(?:([^\/\:]+):)?([^\/]+))/g.exec(arg2.value as string || ''); 
+
+  if (!pathParts || !referenceValueParts || pathParts.length === 0 || referenceValueParts.length === 0) {
+    throw new Error('derived-from or derived-from-or-self requires first argument PATH and second argument IDENTITY.');
+  }
+
+  if (pathParts[0][1]?.startsWith('..') || pathParts[0][1]?.startsWith('/')) {
+    throw new Error('derived-from or derived-from-or-self currently only supports relative paths.');
+  }
+
+  const { configuration: { deviceDescription: { modules } } } = storeService.applicationStore.state;
+  const dataValue = pathParts.reduce((acc, [ns, prop]) => {
+    if (prop === '.') {
+      return acc;
+    } 
+    if (acc && prop) {
+      const moduleName = ns && (Object.values(modules).find((m: Module) => m.prefix === ns) || Object.values(modules).find((m: Module) => m.name === ns))?.name;
+      return (moduleName) ? acc[`${moduleName}:${prop}`] ||  acc[prop] : acc[prop];
+    }
+    return undefined;
+  }, viewData);
+
+  let dataValueParts = dataValue && /(?:(?:([^\/\:]+):)?([^\/]+))/g.exec(dataValue);
+  if (!dataValueParts || dataValueParts.length < 2) {
+    throw new Error(`derived-from or derived-from-or-self value referenced by first argument [${arg1.value}] not found.`);
+  }
+  let [, dataValueNs, dataValueProp] = dataValueParts;
+  let dataValueModule: Module = dataValueNs && (Object.values(modules).find((m: Module) => m.name === dataValueNs));
+  let dataValueIdentity = dataValueModule && dataValueModule.identities && (Object.values(dataValueModule.identities).find((i) => i.label === dataValueProp));
+
+  if (!dataValueIdentity) {
+    throw new Error(`derived-from or derived-from-or-self identity [${dataValue}] referenced by first argument [${arg1.value}] not found.`);
+  }
+
+  const [, referenceValueNs, referenceValueProp] = referenceValueParts;
+  const referenceValueModule = referenceValueNs && (Object.values(modules).find((m: Module) => m.prefix === referenceValueNs));
+  const referenceValueIdentity = referenceValueModule && referenceValueModule.identities && (Object.values(referenceValueModule.identities).find((i) => i.label === referenceValueProp));
+
+  if (!referenceValueIdentity) {
+    throw new Error(`derived-from or derived-from-or-self identity [${arg2.value}] referenced by second argument not found.`);
+  }
+
+  let result = includeSelf && (referenceValueIdentity === dataValueIdentity);
+  while (dataValueIdentity && dataValueIdentity.base && !result) {
+    dataValueParts = dataValue && /(?:(?:([^\/\:]+):)?([^\/]+))/g.exec(dataValueIdentity.base);
+    const [, innerDataValueNs, innerDataValueProp] = dataValueParts;
+    dataValueModule = innerDataValueNs && (Object.values(modules).find((m: Module) => m.prefix === innerDataValueNs)) || dataValueModule;
+    dataValueIdentity = dataValueModule && dataValueModule.identities && (Object.values(dataValueModule.identities).find((i) => i.label === innerDataValueProp)) ;
+    result = (referenceValueIdentity === dataValueIdentity);
+  }
+
+  return result;
+};
+
+const evaluateWhen = async (vPath: string, when: WhenAST, viewData: any): Promise<boolean> => {
+  switch (when.type) {
+    case WhenTokenType.FUNCTION:
+      switch (when.name) {
+        case 'derived-from-or-self':
+          return derivedFrom(vPath, when, viewData, true);
+        case 'derived-from':
+          return derivedFrom(vPath, when, viewData, false);
+        default:
+          throw new Error(`Unknown function ${when.name}`);
+      }
+    case WhenTokenType.AND:
+      return !when.left || !when.right || (await evaluateWhen(vPath, when.left, viewData) && await evaluateWhen(vPath, when.right, viewData));
+    case WhenTokenType.OR:
+      return !when.left || !when.right || (await evaluateWhen(vPath, when.left, viewData) || await evaluateWhen(vPath, when.right, viewData));
+    case WhenTokenType.NOT:
+      return !when.right || ! await evaluateWhen(vPath, when.right, viewData);
+    case WhenTokenType.EXPRESSION:
+      return !(when.value && typeof when.value !== 'string') || await evaluateWhen(vPath, when.value, viewData);
+  }   
+  return true;
+};
+
+export const getReferencedDataList = async (refPath: string, dataPath: string, modules: { [name: string]: Module }, views: ViewSpecification[]) => {
+  const pathParts = splitVPath(refPath, /(?:(?:([^\/\:]+):)?([^\/]+))/g);  // 1 = opt: namespace / 2 = property
+  const defaultNS = pathParts[0][0];
+  let referencedModule = modules[defaultNS];
+
+  let dataMember: string;
+  let view: ViewSpecification;
+  let currentNS: string | null = null;
+  let dataUrls = [dataPath];
+  let data: any;
+
+  for (let i = 0; i < pathParts.length; ++i) {
+    const [pathPartNS, pathPart] = pathParts[i];
+    const namespace = pathPartNS != null ? (currentNS = pathPartNS) : currentNS;
+
+    const viewElement = i === 0
+      ? views[0].elements[`${referencedModule.name}:${pathPart}`]
+      : view!.elements[`${pathPart}`] || view!.elements[`${namespace}:${pathPart}`];
+
+    if (!viewElement) throw new Error(`Could not find ${pathPart} in ${refPath}`);
+    if (i < pathParts.length - 1) {
+      if (!isViewElementObjectOrList(viewElement)) {
+        throw Error(`Module: [${referencedModule.name}].[${viewElement.label}]. View element is not list or object.`);
+      }
+      view = views[+viewElement.viewId];
+      const resultingDataUrls : string[] = [];
+      if (isViewElementList(viewElement)) {
+        for (let j = 0; j < dataUrls.length; ++j) {
+          const dataUrl = dataUrls[j];
+          const restResult = (await restService.getConfigData(dataUrl));
+          if (restResult.data == null || checkResponseCode(restResult)) {
+            const message = restResult.data && restResult.data.errors && restResult.data.errors.error && restResult.data.errors.error[0] && restResult.data.errors.error[0]['error-message'] || '';
+            throw new Error(`Server Error. Status: [${restResult.status}]\n${message || restResult.message || ''}`);
+          }
+
+          let dataRaw = restResult.data[`${defaultNS}:${dataMember!}`];
+          if (dataRaw === undefined) {
+            dataRaw = restResult.data[dataMember!];
+          }
+          dataRaw = dataRaw instanceof Array
+            ? dataRaw[0]
+            : dataRaw;
+
+          data = dataRaw && dataRaw[viewElement.label] || [];
+          const keys: string[] = data.map((entry: { [key: string]: any } )=> entry[viewElement.key!]);
+          resultingDataUrls.push(...keys.map(key => `${dataUrl}/${viewElement.label.replace(/\//ig, '%2F')}=${key.replace(/\//ig, '%2F')}`));
+        }
+        dataMember = viewElement.label;
+      } else {
+        // just a member, not a list
+        const pathSegment = (i === 0
+          ? `/${referencedModule.name}:${viewElement.label.replace(/\//ig, '%2F')}`
+          : `/${viewElement.label.replace(/\//ig, '%2F')}`);
+        resultingDataUrls.push(...dataUrls.map(dataUrl => dataUrl + pathSegment));
+        dataMember = viewElement.label;
+      }
+      dataUrls = resultingDataUrls;
+    } else {
+      data = [];
+      for (let j = 0; j < dataUrls.length; ++j) {
+        const dataUrl = dataUrls[j];
+        const restResult = (await restService.getConfigData(dataUrl));
+        if (restResult.data == null || checkResponseCode(restResult)) {
+          const message = restResult.data && restResult.data.errors && restResult.data.errors.error && restResult.data.errors.error[0] && restResult.data.errors.error[0]['error-message'] || '';
+          throw new Error(`Server Error. Status: [${restResult.status}]\n${message || restResult.message || ''}`);
+        }
+        let dataRaw = restResult.data[`${defaultNS}:${dataMember!}`];
+        if (dataRaw === undefined) {
+          dataRaw = restResult.data[dataMember!];
+        }
+        dataRaw = dataRaw instanceof Array
+          ? dataRaw[0]
+          : dataRaw;
+        data.push(dataRaw);
+      }
+      // BUG UUID ist nicht in den elements enthalten !!!!!!
+      const key = viewElement && viewElement.label || pathPart;
+      return {
+        view: view!,
+        data: data,
+        key: key,
+      };
+    }
+  }
+  return null;
+};
+
+export const resolveViewDescription = (defaultNS: string | null, vPath: string, view: ViewSpecification): ViewSpecification =>{
+
+  // resolve all references.
+  view = { ...view };
+  view.elements = Object.keys(view.elements).reduce<{ [name: string]: ViewElement }>((acc, cur) => {
+    const resolveHistory : ViewElement[] = [];  
+    let elm = view.elements[cur];
+    const key = defaultNS && cur.replace(new RegExp(`^${defaultNS}:`, 'i'), '') || cur;
+    while (isViewElementReference(elm)) {
+      const result = (elm.ref(vPath));  
+      if (result) {
+        const [referencedElement, referencedPath] = result;
+        if (resolveHistory.some(hist => hist === referencedElement)) {
+          console.error(`Circle reference found at: ${vPath}`, resolveHistory);
+          break;
+        }
+        elm = referencedElement;
+        vPath = referencedPath;
+        resolveHistory.push(elm);
+      }
+    } 
+    
+    acc[key] = { ...elm, id: key };
+    
+    return acc;
+  }, {});
+  return view;
+};
+
+export const flattenViewElements = (defaultNS: string | null, parentPath: string, elements: { [name: string]: ViewElement }, views: ViewSpecification[], currentPath: string ): { [name: string]: ViewElement } => {
+  if (!elements) return {};
+  return Object.keys(elements).reduce<{ [name: string]: ViewElement }>((acc, cur) => {
+    const elm = elements[cur];
+
+    // remove the default namespace, and only the default namespace, sine it seems that this is also not in the restconf response
+    const elmKey = defaultNS && elm.id.replace(new RegExp(`^${defaultNS}:`, 'i'), '') || elm.id;
+    const key = parentPath ? `${parentPath}.${elmKey}` : elmKey;
+
+    if (isViewElementRpc(elm)) {
+      console.warn(`Flatten of RFC not supported ! [${currentPath}][${elm.label}]`);
+      return acc;
+    } else if (isViewElementObjectOrList(elm)) {
+      const view = views[+elm.viewId];
+      const inner = view && flattenViewElements(defaultNS, key, view.elements, views, `${currentPath}/${view.name}`);
+      if (inner) {
+        Object.keys(inner).forEach(k => (acc[k] = inner[k]));
+      }
+    } else if (isViewElementChoice(elm)) {
+      acc[key] = {
+        ...elm,
+        id: key,
+        cases: Object.keys(elm.cases).reduce<{ [name: string]: ViewElementChoiceCase }>((accCases, curCases) => {
+          const caseElement = elm.cases[curCases];
+          accCases[curCases] = {
+            ...caseElement,
+            // Hint: do not use key it contains elmKey, which shell be omitted for cases.
+            elements: flattenViewElements(defaultNS, /*key*/ parentPath, caseElement.elements, views, `${currentPath}/${elm.label}`),
+          };
+          return accCases;
+        }, {}),
+      };
+    } else {
+      acc[key] = {
+        ...elm,
+        id: key,
+      };
+    }
+    return acc;
+  }, {});
+};
+
+export const filterViewElements = async (vPath: string, viewData: any, viewSpecification: ViewSpecification) => {
+  // filter elements of viewSpecification by evaluating when property
+  return Object.keys(viewSpecification.elements).reduce(async (accPromise, cur) => {
+    const acc = await accPromise;
+    const elm = viewSpecification.elements[cur];
+    if (!elm.when || await evaluateWhen(vPath || '', elm.when, viewData).catch((ex) => {
+      console.warn(`Error evaluating when clause at: ${viewSpecification.name} for element: ${cur}`, ex);
+      return true;
+    })) {
+      acc.elements[cur] = elm;
+    }
+    return acc;
+  }, Promise.resolve({ ...viewSpecification, elements: {} as { [key: string]: ViewElement } }));
+};
\ No newline at end of file
index 0e2ddb3..0f143d8 100644 (file)
@@ -25,17 +25,41 @@ import { WithStyles } from '@mui/styles';
 import withStyles from '@mui/styles/withStyles';
 import createStyles from '@mui/styles/createStyles';
 
-import connect, { IDispatcher, Connect } from "../../../../framework/src/flux/connect";
-import { IApplicationStoreState } from "../../../../framework/src/store/applicationStore";
-import MaterialTable, { ColumnModel, ColumnType, MaterialTableCtorType } from "../../../../framework/src/components/material-table";
-import { Loader } from "../../../../framework/src/components/material-ui/loader";
+import { useConfirm } from 'material-ui-confirm';
+
+import { connect, IDispatcher, Connect } from '../../../../framework/src/flux/connect';
+import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
+import MaterialTable, { ColumnModel, ColumnType, MaterialTableCtorType } from '../../../../framework/src/components/material-table';
+import { Loader } from '../../../../framework/src/components/material-ui/loader';
 import { renderObject } from '../../../../framework/src/components/objectDump';
 
 import { DisplayModeType } from '../handlers/viewDescriptionHandler';
-import { SetSelectedValue, splitVPath, updateDataActionAsyncCreator, updateViewActionAsyncCreator, removeElementActionAsyncCreator, executeRpcActionAsyncCreator } from "../actions/deviceActions";
-import { ViewSpecification, isViewElementString, isViewElementNumber, isViewElementBoolean, isViewElementObjectOrList, isViewElementSelection, isViewElementChoise, ViewElement, ViewElementChoise, isViewElementUnion, isViewElementRpc, ViewElementRpc, isViewElementEmpty, isViewElementDate } from "../models/uiModels";
-
-import { getAccessPolicyByUrl } from "../../../../framework/src/services/restService";
+import {
+  SetSelectedValue,
+  updateDataActionAsyncCreator,
+  updateViewActionAsyncCreator,
+  removeElementActionAsyncCreator,
+  executeRpcActionAsyncCreator,
+} from '../actions/deviceActions';
+
+import {
+  ViewElement,
+  ViewSpecification,
+  ViewElementChoice,
+  ViewElementRpc,
+  isViewElementString,
+  isViewElementNumber,
+  isViewElementBoolean,
+  isViewElementObjectOrList,
+  isViewElementSelection,
+  isViewElementChoice,
+  isViewElementUnion,
+  isViewElementRpc,
+  isViewElementEmpty,
+  isViewElementDate,
+} from '../models/uiModels';
+
+import { getAccessPolicyByUrl } from '../../../../framework/src/services/restService';
 
 import Fab from '@mui/material/Fab';
 import AddIcon from '@mui/icons-material/Add';
@@ -44,23 +68,22 @@ import ArrowBack from '@mui/icons-material/ArrowBack';
 import RemoveIcon from '@mui/icons-material/RemoveCircleOutline';
 import SaveIcon from '@mui/icons-material/Save';
 import EditIcon from '@mui/icons-material/Edit';
-import Tooltip from "@mui/material/Tooltip";
-import FormControl from "@mui/material/FormControl";
-import IconButton from "@mui/material/IconButton";
-
-import InputLabel from "@mui/material/InputLabel";
-import Select from "@mui/material/Select";
-import MenuItem from "@mui/material/MenuItem";
-import Breadcrumbs from "@mui/material/Breadcrumbs";
+import Tooltip from '@mui/material/Tooltip';
+import FormControl from '@mui/material/FormControl';
+import IconButton from '@mui/material/IconButton';
+
+import InputLabel from '@mui/material/InputLabel';
+import Select from '@mui/material/Select';
+import MenuItem from '@mui/material/MenuItem';
+import Breadcrumbs from '@mui/material/Breadcrumbs';
 import Button from '@mui/material/Button';
-import Link from "@mui/material/Link";
+import Link from '@mui/material/Link';
 import Accordion from '@mui/material/Accordion';
 import AccordionSummary from '@mui/material/AccordionSummary';
 import AccordionDetails from '@mui/material/AccordionDetails';
 import Typography from '@mui/material/Typography';
 import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
 
-
 import { BaseProps } from '../components/baseProps';
 import { UIElementReference } from '../components/uiElementReference';
 import { UiElementNumber } from '../components/uiElementNumber';
@@ -70,44 +93,43 @@ import { UiElementSelection } from '../components/uiElementSelection';
 import { UIElementUnion } from '../components/uiElementUnion';
 import { UiElementLeafList } from '../components/uiElementLeafList';
 
-import { useConfirm } from 'material-ui-confirm';
-import restService from '../services/restServices';
+import { splitVPath } from '../utilities/viewEngineHelper';
 
 const styles = (theme: Theme) => createStyles({
   header: {
-    "display": "flex",
-    "justifyContent": "space-between",
+    'display': 'flex',
+    'justifyContent': 'space-between',
   },
   leftButton: {
-    "justifyContent": "left"
+    'justifyContent': 'left',
   },
   outer: {
-    "flex": "1",
-    "height": "100%",
-    "display": "flex",
-    "alignItems": "center",
-    "justifyContent": "center",
+    'flex': '1',
+    'height': '100%',
+    'display': 'flex',
+    'alignItems': 'center',
+    'justifyContent': 'center',
   },
   inner: {
 
   },
   container: {
-    "height": "100%",
-    "display": "flex",
-    "flexDirection": "column",
+    'height': '100%',
+    'display': 'flex',
+    'flexDirection': 'column',
   },
-  "icon": {
-    "marginRight": theme.spacing(0.5),
-    "width": 20,
-    "height": 20,
+  'icon': {
+    'marginRight': theme.spacing(0.5),
+    'width': 20,
+    'height': 20,
   },
-  "fab": {
-    "margin": theme.spacing(1),
+  'fab': {
+    'margin': theme.spacing(1),
   },
   button: {
     margin: 0,
-    padding: "6px 6px",
-    minWidth: 'unset'
+    padding: '6px 6px',
+    minWidth: 'unset',
   },
   readOnly: {
     '& label.Mui-focused': {
@@ -129,29 +151,29 @@ const styles = (theme: Theme) => createStyles({
     },
   },
   uiView: {
-    overflowY: "auto",
+    overflowY: 'auto',
   },
   section: {
-    padding: "15px",
+    padding: '15px',
     borderBottom: `2px solid ${theme.palette.divider}`,
   },
   viewElements: {
-    width: 485, marginLeft: 20, marginRight: 20
+    width: 485, marginLeft: 20, marginRight: 20,
   },
   verificationElements: {
-    width: 485, marginLeft: 20, marginRight: 20
+    width: 485, marginLeft: 20, marginRight: 20,
   },
   heading: {
     fontSize: theme.typography.pxToRem(15),
     fontWeight: theme.typography.fontWeightRegular,
   },
   moduleCollection: {
-    marginTop: "16px",
-    overflow: "auto",
+    marginTop: '16px',
+    overflow: 'auto',
   },
   objectReult: {
-    overflow: "auto"
-  }
+    overflow: 'auto',
+  },
 });
 
 const mapProps = (state: IApplicationStoreState) => ({
@@ -183,10 +205,10 @@ type ConfigurationApplicationComponentState = {
   editMode: boolean;
   canEdit: boolean;
   viewData: { [key: string]: any } | null;
-  choises: { [path: string]: { selectedCase: string, data: { [property: string]: any } } };
-}
+  choices: { [path: string]: { selectedCase: string; data: { [property: string]: any } } };
+};
 
-type GetStatelessComponentProps<T> = T extends (props: infer P & { children?: React.ReactNode }) => any ? P : any
+type GetStatelessComponentProps<T> = T extends (props: infer P & { children?: React.ReactNode }) => any ? P : any;
 const AccordionSummaryExt: React.FC<GetStatelessComponentProps<typeof AccordionSummary>> = (props) => {
   const [disabled, setDisabled] = useState(true);
   const onMouseDown = (ev: React.MouseEvent<HTMLElement>) => {
@@ -202,7 +224,7 @@ const AccordionSummaryExt: React.FC<GetStatelessComponentProps<typeof AccordionS
   );
 };
 
-const OldProps = Symbol("OldProps");
+const OldProps = Symbol('OldProps');
 class ConfigurationApplicationComponent extends React.Component<ConfigurationApplicationComponentProps, ConfigurationApplicationComponentState> {
 
   /**
@@ -216,17 +238,17 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
       canEdit: false,
       editMode: false,
       viewData: null,
-      choises: {},
-    }
+      choices: {},
+    };
   }
 
-  private static getChoisesFromElements = (elements: { [name: string]: ViewElement }, viewData: any) => {
+  private static getChoicesFromElements = (elements: { [name: string]: ViewElement }, viewData: any) => {
     return Object.keys(elements).reduce((acc, cur) => {
       const elm = elements[cur];
-      if (isViewElementChoise(elm)) {
+      if (isViewElementChoice(elm)) {
         const caseKeys = Object.keys(elm.cases);
 
-        // find the right case for this choise, use the first one with data, at least use index 0
+        // find the right case for this choice, use the first one with data, at least use index 0
         const selectedCase = caseKeys.find(key => {
           const caseElm = elm.cases[key];
           return Object.keys(caseElm.elements).some(caseElmKey => {
@@ -255,26 +277,26 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
         };
       }
       return acc;
-    }, {} as { [path: string]: { selectedCase: string, data: { [property: string]: any } } }) || {}
-  }
+    }, {} as { [path: string]: { selectedCase: string; data: { [property: string]: any } } }) || {};
+  };
 
   static getDerivedStateFromProps(nextProps: ConfigurationApplicationComponentProps, prevState: ConfigurationApplicationComponentState & { [OldProps]: ConfigurationApplicationComponentProps }) {
 
     if (!prevState || !prevState[OldProps] || (prevState[OldProps].viewData !== nextProps.viewData)) {
-      const isNew: boolean = nextProps.vPath?.endsWith("[]") || false;
+      const isNew: boolean = nextProps.vPath?.endsWith('[]') || false;
       const state = {
         ...prevState,
         isNew: isNew,
         editMode: isNew,
         viewData: nextProps.viewData || null,
         [OldProps]: nextProps,
-        choises: nextProps.displaySpecification.displayMode === DisplayModeType.doNotDisplay
+        choices: nextProps.displaySpecification.displayMode === DisplayModeType.doNotDisplay
           || nextProps.displaySpecification.displayMode === DisplayModeType.displayAsMessage
           ? null
           : nextProps.displaySpecification.displayMode === DisplayModeType.displayAsRPC
-            ? nextProps.displaySpecification.inputViewSpecification && ConfigurationApplicationComponent.getChoisesFromElements(nextProps.displaySpecification.inputViewSpecification.elements, nextProps.viewData) || []
-            : ConfigurationApplicationComponent.getChoisesFromElements(nextProps.displaySpecification.viewSpecification.elements, nextProps.viewData)
-      }
+            ? nextProps.displaySpecification.inputViewSpecification && ConfigurationApplicationComponent.getChoicesFromElements(nextProps.displaySpecification.inputViewSpecification.elements, nextProps.viewData) || []
+            : ConfigurationApplicationComponent.getChoicesFromElements(nextProps.displaySpecification.viewSpecification.elements, nextProps.viewData),
+      };
       return state;
     }
     return null;
@@ -282,24 +304,24 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
 
   private navigate = (path: string) => {
     this.props.history.push(`${this.props.match.url}${path}`);
-  }
+  };
 
   private changeValueFor = (property: string, value: any) => {
     this.setState({
       viewData: {
         ...this.state.viewData,
-        [property]: value
-      }
+        [property]: value,
+      },
     });
-  }
+  };
 
   private collectData = (elements: { [name: string]: ViewElement }) => {
-    // ensure only active choises will be contained
+    // ensure only active choices will be contained
     const viewData: { [key: string]: any } = { ...this.state.viewData };
-    const choiseKeys = Object.keys(elements).filter(elmKey => isViewElementChoise(elements[elmKey]));
-    const elementsToRemove = choiseKeys.reduce((acc, curChoiceKey) => {
-      const currentChoice = elements[curChoiceKey] as ViewElementChoise;
-      const selectedCase = this.state.choises[curChoiceKey].selectedCase;
+    const choiceKeys = Object.keys(elements).filter(elmKey => isViewElementChoice(elements[elmKey]));
+    const elementsToRemove = choiceKeys.reduce((acc, curChoiceKey) => {
+      const currentChoice = elements[curChoiceKey] as ViewElementChoice;
+      const selectedCase = this.state.choices[curChoiceKey].selectedCase;
       Object.keys(currentChoice.cases).forEach(caseKey => {
         const caseElements = currentChoice.cases[caseKey].elements;
         if (caseKey === selectedCase) {
@@ -311,7 +333,7 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
             }
           });
           return;
-        };
+        }
         Object.keys(caseElements).forEach(caseElementKey => {
           acc.push(caseElements[caseElementKey]);
         });
@@ -325,17 +347,17 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
       }
       return acc;
     }, {} as { [key: string]: any });
-  }
+  };
 
   private isPolicyViewElementForbidden = (element: ViewElement, dataPath: string): boolean => {
     const policy = getAccessPolicyByUrl(`${dataPath}/${element.id}`);
     return !(policy.GET && policy.POST);
-  }
+  };
 
   private isPolicyModuleForbidden = (moduleName: string, dataPath: string): boolean => {
     const policy = getAccessPolicyByUrl(`${dataPath}/${moduleName}`);
     return !(policy.GET && policy.POST);
-  }
+  };
 
   private getEditorForViewElement = (uiElement: ViewElement): (null | React.ComponentType<BaseProps<any>>) => {
     if (isViewElementEmpty(uiElement)) {
@@ -353,12 +375,12 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
     } else if (isViewElementUnion(uiElement)) {
       return UIElementUnion;
     } else {
-      if (process.env.NODE_ENV !== "production") {
-        console.error(`Unknown element type - ${(uiElement as any).uiType} in ${(uiElement as any).id}.`)
+      if (process.env.NODE_ENV !== 'production') {
+        console.error(`Unknown element type - ${(uiElement as any).uiType} in ${(uiElement as any).id}.`);
       }
       return null;
     }
-  }
+  };
 
   private renderUIElement = (uiElement: ViewElement, viewData: { [key: string]: any }, keyProperty: string | undefined, editMode: boolean, isNew: boolean) => {
     const isKey = (uiElement.label === keyProperty);
@@ -377,7 +399,7 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
         value={uiElement}
         readOnly={!canEdit}
         disabled={editMode && !canEdit}
-        onChange={(e) => { this.changeValueFor(uiElement.id, e) }}
+        onChange={(e) => { this.changeValueFor(uiElement.id, e); }}
         getEditorForViewElement={this.getEditorForViewElement}
       />;
     } else {
@@ -391,7 +413,7 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
             value={uiElement}
             readOnly={!canEdit}
             disabled={editMode && !canEdit}
-            onChange={(e) => { this.changeValueFor(uiElement.id, e) }}
+            onChange={(e) => { this.changeValueFor(uiElement.id, e); }}
           />)
         : null;
     }
@@ -418,14 +440,14 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
   //   }
   // };
 
-  private renderUIChoise = (uiElement: ViewElementChoise, viewData: { [key: string]: any }, keyProperty: string | undefined, editMode: boolean, isNew: boolean) => {
+  private renderUIChoice = (uiElement: ViewElementChoice, viewData: { [key: string]: any }, keyProperty: string | undefined, editMode: boolean, isNew: boolean) => {
     const isKey = (uiElement.label === keyProperty);
 
-    const currentChoise = this.state.choises[uiElement.id];
-    const currentCase = currentChoise && uiElement.cases[currentChoise.selectedCase];
+    const currentChoice = this.state.choices[uiElement.id];
+    const currentCase = currentChoice && uiElement.cases[currentChoice.selectedCase];
 
     const canEdit = editMode && (isNew || (uiElement.config && !isKey));
-    if (isViewElementChoise(uiElement)) {
+    if (isViewElementChoice(uiElement)) {
       const subElements = currentCase?.elements;
       return (
         <>
@@ -435,14 +457,14 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
               aria-label={uiElement.label + '-selection'}
               required={!!uiElement.mandatory}
               onChange={(e) => {
-                if (currentChoise.selectedCase === e.target.value) {
+                if (currentChoice.selectedCase === e.target.value) {
                   return; // nothing changed
                 }
-                this.setState({ choises: { ...this.state.choises, [uiElement.id]: { ...this.state.choises[uiElement.id], selectedCase: e.target.value as string } } });
+                this.setState({ choices: { ...this.state.choices, [uiElement.id]: { ...this.state.choices[uiElement.id], selectedCase: e.target.value as string } } });
               }}
               readOnly={!canEdit}
               disabled={editMode && !canEdit}
-              value={this.state.choises[uiElement.id].selectedCase}
+              value={this.state.choices[uiElement.id].selectedCase}
               inputProps={{
                 name: uiElement.id,
                 id: `select-${uiElement.id}`,
@@ -452,7 +474,7 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
                 Object.keys(uiElement.cases).map(caseKey => {
                   const caseElm = uiElement.cases[caseKey];
                   return (
-                    <MenuItem key={caseElm.id} value={caseKey} aria-label={caseKey}><Tooltip title={caseElm.description || ''}><div style={{ width: "100%" }}>{caseElm.label}</div></Tooltip></MenuItem>
+                    <MenuItem key={caseElm.id} value={caseKey} aria-label={caseKey}><Tooltip title={caseElm.description || ''}><div style={{ width: '100%' }}>{caseElm.label}</div></Tooltip></MenuItem>
                   );
                 })
               }
@@ -463,13 +485,13 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
               const elm = subElements[elmKey];
               return this.renderUIElement(elm, viewData, keyProperty, editMode, isNew);
             })
-            : <h3>Invalid Choise</h3>
+            : <h3>Invalid Choice</h3>
           }
         </>
       );
     } else {
-      if (process.env.NODE_ENV !== "production") {
-        console.error(`Unknown type - ${(uiElement as any).uiType} in ${(uiElement as any).id}.`)
+      if (process.env.NODE_ENV !== 'production') {
+        console.error(`Unknown type - ${(uiElement as any).uiType} in ${(uiElement as any).id}.`);
       }
       return null;
     }
@@ -478,8 +500,6 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
   private renderUIView = (viewSpecification: ViewSpecification, dataPath: string, viewData: { [key: string]: any }, keyProperty: string | undefined, editMode: boolean, isNew: boolean) => {
     const { classes } = this.props;
 
-
-
     const orderFunc = (vsA: ViewElement, vsB: ViewElement) => {
       if (keyProperty) {
         // if (vsA.label === vsB.label) return 0;
@@ -497,15 +517,15 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
       const elm = viewSpecification.elements[cur];
       if (isViewElementObjectOrList(elm)) {
         acc.references.push(elm);
-      } else if (isViewElementChoise(elm)) {
-        acc.choises.push(elm);
+      } else if (isViewElementChoice(elm)) {
+        acc.choices.push(elm);
       } else if (isViewElementRpc(elm)) {
         acc.rpcs.push(elm);
       } else {
         acc.elements.push(elm);
       }
       return acc;
-    }, { elements: [] as ViewElement[], references: [] as ViewElement[], choises: [] as ViewElementChoise[], rpcs: [] as ViewElementRpc[] });
+    }, { elements: [] as ViewElement[], references: [] as ViewElement[], choices: [] as ViewElementChoice[], rpcs: [] as ViewElementRpc[] });
 
     sections.elements = sections.elements.sort(orderFunc);
 
@@ -523,15 +543,15 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
           ? (
             <div className={classes.section}>
               {sections.references.map(element => (
-                <UIElementReference key={element.id} element={element} disabled={editMode || this.isPolicyViewElementForbidden(element, dataPath)} onOpenReference={(elm) => { this.navigate(`/${elm.id}`) }} />
+                <UIElementReference key={element.id} element={element} disabled={editMode || this.isPolicyViewElementForbidden(element, dataPath)} onOpenReference={(elm) => { this.navigate(`/${elm.id}`); }} />
               ))}
             </div>
           ) : null
         }
-        {sections.choises.length > 0
+        {sections.choices.length > 0
           ? (
             <div className={classes.section}>
-              {sections.choises.map(element => this.renderUIChoise(element, viewData, keyProperty, editMode, isNew))}
+              {sections.choices.map(element => this.renderUIChoice(element, viewData, keyProperty, editMode, isNew))}
             </div>
           ) : null
         }
@@ -539,7 +559,7 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
           ? (
             <div className={classes.section}>
               {sections.rpcs.map(element => (
-                <UIElementReference key={element.id} element={element} disabled={editMode || this.isPolicyViewElementForbidden(element, dataPath)} onOpenReference={(elm) => { this.navigate(`/${elm.id}`) }} />
+                <UIElementReference key={element.id} element={element} disabled={editMode || this.isPolicyViewElementForbidden(element, dataPath)} onOpenReference={(elm) => { this.navigate(`/${elm.id}`); }} />
               ))}
             </div>
           ) : null
@@ -550,6 +570,7 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
 
   private renderUIViewSelector = (viewSpecification: ViewSpecification, dataPath: string, viewData: { [key: string]: any }, keyProperty: string | undefined, editMode: boolean, isNew: boolean) => {
     const { classes } = this.props;
+    
     // group by module name
     const modules = Object.keys(viewSpecification.elements).reduce<{ [key: string]: ViewSpecification }>((acc, cur) => {
       const elm = viewSpecification.elements[cur];
@@ -565,6 +586,7 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
         {
           moduleKeys.map(key => {
             const moduleView = modules[key];
+            
             return (
               <Accordion key={key} defaultExpanded={moduleKeys.length < 4} aria-label={key + '-panel'} >
                 <AccordionSummaryExt expandIcon={<ExpandMoreIcon />} aria-controls={`content-${key}`} id={`header-${key}`} disabled={this.isPolicyModuleForbidden(`${key}:`, dataPath)} >
@@ -584,8 +606,8 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
   private renderUIViewList(listSpecification: ViewSpecification, dataPath: string, listKeyProperty: string, apiDocPath: string, listData: { [key: string]: any }[]) {
     const listElements = listSpecification.elements;
     const apiDocPathCreate = apiDocPath  ? `${location.origin}${apiDocPath
-      .replace("$$$standard$$$", "topology-netconfnode%20resources%20-%20RestConf%20RFC%208040")
-      .replace("$$$action$$$", "put")}${listKeyProperty ? `_${listKeyProperty.replace(/[\/=\-\:]/g, '_')}_` : '' }` : undefined;
+      .replace('$$$standard$$$', 'topology-netconfnode%20resources%20-%20RestConf%20RFC%208040')
+      .replace('$$$action$$$', 'put')}${listKeyProperty ? `_${listKeyProperty.replace(/[\/=\-\:]/g, '_')}_` : '' }` : undefined;
 
     const config = listSpecification.config && listKeyProperty; // We can not configure a list with no key.
 
@@ -598,7 +620,7 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
       tooltip: 'Add',
       ariaLabel:'add-element',
       onClick: () => {
-        navigate("[]"); // empty key means new element
+        navigate('[]'); // empty key means new element
       },
       disabled: !config,
     };
@@ -615,11 +637,11 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
 
     const { classes, removeElement } = this.props;
 
-    const DeleteIconWithConfirmation: React.FC<{disabled?: boolean, rowData: { [key: string]: any }, onReload: () => void }> = (props) => {
+    const DeleteIconWithConfirmation: React.FC<{ disabled?: boolean; rowData: { [key: string]: any }; onReload: () => void }> = (props) => {
       const confirm = useConfirm();
 
       return (
-        <Tooltip disableInteractive title={"Remove"} >
+        <Tooltip disableInteractive title={'Remove'} >
           <IconButton
             disabled={props.disabled}
             className={classes.button}
@@ -627,15 +649,15 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
             onClick={async (e) => {
               e.stopPropagation();
               e.preventDefault();
-              confirm({ title: "Do you really want to delete this element ?", description: "This action is permanent!", confirmationButtonProps: { color: "secondary" }, cancellationButtonProps: { color:"inherit" } })
-              .then(() => {
-                 let keyId = "";
-                 if (listKeyProperty && listKeyProperty.split(" ").length > 1) {
-                 keyId += listKeyProperty.split(" ").map(id => props.rowData[id]).join(",");
+              confirm({ title: 'Do you really want to delete this element ?', description: 'This action is permanent!', confirmationButtonProps: { color: 'secondary' }, cancellationButtonProps: { color:'inherit' } })
+                .then(() => {
+                  let keyId = '';
+                  if (listKeyProperty && listKeyProperty.split(' ').length > 1) {
+                    keyId += listKeyProperty.split(' ').map(id => props.rowData[id]).join(',');
                   } else {
-                   keyId = props.rowData[listKeyProperty];
-                    }
-                   return removeElement(`${this.props.vPath}[${keyId}]`)
+                    keyId = props.rowData[listKeyProperty];
+                  }
+                  return removeElement(`${this.props.vPath}[${keyId}]`);
                 }).then(props.onReload);
             }}
             size="large">
@@ -643,44 +665,46 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
           </IconButton>
         </Tooltip>
       );
-    }
+    };
 
     return (
       <SelectElementTable stickyHeader idProperty={listKeyProperty} tableId={null} rows={listData} customActionButtons={apiDocPathCreate ? [addNewElementAction, addWithApiDocElementAction] : [addNewElementAction]} columns={
         Object.keys(listElements).reduce<ColumnModel<{ [key: string]: any }>[]>((acc, cur) => {
           const elm = listElements[cur];
-          if (elm.uiType !== "object" && listData.every(entry => entry[elm.label] != null)) {
+          if (elm.uiType !== 'object' && listData.every(entry => entry[elm.label] != null)) {
             if (elm.label !== listKeyProperty) {
-              acc.push(elm.uiType === "boolean"
+              acc.push(elm.uiType === 'boolean'
                 ? { property: elm.label, type: ColumnType.boolean }
-                : elm.uiType === "date"
+                : elm.uiType === 'date'
                   ? { property: elm.label, type: ColumnType.date }
-                  : { property: elm.label, type: elm.uiType === "number" ? ColumnType.numeric : ColumnType.text });
+                  : { property: elm.label, type: elm.uiType === 'number' ? ColumnType.numeric : ColumnType.text });
             } else {
-              acc.unshift(elm.uiType === "boolean"
+              acc.unshift(elm.uiType === 'boolean'
                 ? { property: elm.label, type: ColumnType.boolean }
-                : elm.uiType === "date"
+                : elm.uiType === 'date'
                   ? { property: elm.label, type: ColumnType.date }
-                  : { property: elm.label, type: elm.uiType === "number" ? ColumnType.numeric : ColumnType.text });
+                  : { property: elm.label, type: elm.uiType === 'number' ? ColumnType.numeric : ColumnType.text });
             }
           }
           return acc;
         }, []).concat([{
-          property: "Actions", disableFilter: true, disableSorting: true, type: ColumnType.custom, customControl: (({ rowData }) => {
+          property: 'Actions', disableFilter: true, disableSorting: true, type: ColumnType.custom, customControl: (({ rowData }) => {
             return (
               <DeleteIconWithConfirmation disabled={!config} rowData={rowData} onReload={() => this.props.vPath && this.props.reloadView(this.props.vPath)} />
             );
-          })
+          }),
         }])
       } onHandleClick={(ev, row) => {
         ev.preventDefault();
-        let keyId = ""
-        if (listKeyProperty && listKeyProperty.split(" ").length > 1) {
-          keyId += listKeyProperty.split(" ").map(id => row[id]).join(",");
+        let keyId = '';
+        if (listKeyProperty && listKeyProperty.split(' ').length > 1) {
+          keyId += listKeyProperty.split(' ').map(id => row[id]).join(',');
         } else {
           keyId = row[listKeyProperty];
         }
-        listKeyProperty && navigate(`[${encodeURIComponent(keyId)}]`); // Do not navigate without key.
+        if (listKeyProperty) {
+          navigate(`[${encodeURIComponent(keyId)}]`); // Do not navigate without key.
+        }
       }} ></SelectElementTable>
     );
   }
@@ -704,17 +728,17 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
     const sections = inputViewSpecification && Object.keys(inputViewSpecification.elements).reduce((acc, cur) => {
       const elm = inputViewSpecification.elements[cur];
       if (isViewElementObjectOrList(elm)) {
-        console.error("Object should not appear in RPC view !");
-      } else if (isViewElementChoise(elm)) {
-        acc.choises.push(elm);
+        console.error('Object should not appear in RPC view !');
+      } else if (isViewElementChoice(elm)) {
+        acc.choices.push(elm);
       } else if (isViewElementRpc(elm)) {
-        console.error("RPC should not appear in RPC view !");
+        console.error('RPC should not appear in RPC view !');
       } else {
         acc.elements.push(elm);
       }
       return acc;
-    }, { elements: [] as ViewElement[], references: [] as ViewElement[], choises: [] as ViewElementChoise[], rpcs: [] as ViewElementRpc[] })
-      || { elements: [] as ViewElement[], references: [] as ViewElement[], choises: [] as ViewElementChoise[], rpcs: [] as ViewElementRpc[] };
+    }, { elements: [] as ViewElement[], references: [] as ViewElement[], choices: [] as ViewElementChoice[], rpcs: [] as ViewElementRpc[] })
+      || { elements: [] as ViewElement[], references: [] as ViewElement[], choices: [] as ViewElementChoice[], rpcs: [] as ViewElementRpc[] };
 
     sections.elements = sections.elements.sort(orderFunc);
 
@@ -728,10 +752,10 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
             </div>
           ) : null
         }
-        { sections.choises.length > 0
+        { sections.choices.length > 0
           ? (
             <div className={classes.section}>
-              {sections.choises.map(element => this.renderUIChoise(element, inputViewData, keyProperty, editMode, isNew))}
+              {sections.choices.map(element => this.renderUIChoice(element, inputViewData, keyProperty, editMode, isNew))}
             </div>
           ) : null
         }
@@ -747,13 +771,13 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
         </div>
       </>
     );
-  };
+  }
 
   private renderBreadCrumps() {
     const { editMode } = this.state;
     const { displaySpecification, vPath, nodeId } = this.props;
     const pathParts = splitVPath(vPath!, /(?:([^\/\["]+)(?:\[([^\]]*)\])?)/g); // 1 = property / 2 = optional key
-    let lastPath = `/configuration`;
+    let lastPath = '/configuration';
     let basePath = `/configuration/${nodeId}`;
     return (
       <div className={this.props.classes.header}>
@@ -774,7 +798,7 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
               pathParts.map(([prop, key], ind) => {
                 const path = `${basePath}/${prop}`;
                 const keyPath = key && `${basePath}/${prop}[${key}]`;
-                const propTitle = prop.replace(/^[^:]+:/, "");
+                const propTitle = prop.replace(/^[^:]+:/, '');
                 const ret = (
                   <span key={ind}>
                     <Link underline="hover" color="inherit" href="#"
@@ -789,8 +813,8 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
                         onClick={(ev: React.MouseEvent<HTMLElement>) => {
                           ev.preventDefault();
                           this.props.history.push(keyPath);
-                        }}>{`[${key && key.replace(/\%2C/g, ",")}]`}</Link> || null
-                    }
+                        }}>{`[${key && key.replace(/\%2C/g, ',')}]`}</Link> || null
+                      }
                   </span>
                 );
                 lastPath = basePath;
@@ -802,7 +826,9 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
         </div>
         {this.state.editMode && (
           <Fab color="secondary" aria-label="back-button" className={this.props.classes.fab} onClick={async () => {
-            this.props.vPath && (await this.props.reloadView(this.props.vPath));
+            if (this.props.vPath) {
+              await this.props.reloadView(this.props.vPath);
+            }
             this.setState({ editMode: false });
           }} ><ArrowBack /></Fab>
         ) || null}
@@ -810,7 +836,7 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
           displaySpecification.displayMode === DisplayModeType.displayAsObject && displaySpecification.viewSpecification.canEdit && (<div>
             <Fab color="secondary" aria-label={editMode ? 'save-button' : 'edit-button'} className={this.props.classes.fab} onClick={() => {
               if (this.state.editMode) {
-                // ensure only active choises will be contained
+                // ensure only active choices will be contained
                 const resultingViewData = this.collectData(displaySpecification.viewSpecification.elements);
                 this.props.onUpdateData(this.props.vPath!, resultingViewData);
               }
@@ -830,7 +856,7 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
   private renderValueSelector() {
     const { listKeyProperty, listSpecification, listData, onValueSelected } = this.props;
     if (!listKeyProperty || !listSpecification) {
-      throw new Error("ListKex ot view not specified.");
+      throw new Error('ListKex ot view not specified.');
     }
 
     return (
@@ -838,11 +864,11 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp
         <SelectElementTable stickyHeader idProperty={listKeyProperty} tableId={null} rows={listData} columns={
           Object.keys(listSpecification.elements).reduce<ColumnModel<{ [key: string]: any }>[]>((acc, cur) => {
             const elm = listSpecification.elements[cur];
-            if (elm.uiType !== "object" && listData.every(entry => entry[elm.label] != null)) {
+            if (elm.uiType !== 'object' && listData.every(entry => entry[elm.label] != null)) {
               if (elm.label !== listKeyProperty) {
-                acc.push({ property: elm.label, type: elm.uiType === "number" ? ColumnType.numeric : ColumnType.text });
+                acc.push({ property: elm.label, type: elm.uiType === 'number' ? ColumnType.numeric : ColumnType.text });
               } else {
-                acc.unshift({ property: elm.label, type: elm.uiType === "number" ? ColumnType.numeric : ColumnType.text });
+                acc.unshift({ property: elm.label, type: elm.uiType === 'number' ? ColumnType.numeric : ColumnType.text });
               }
             }
             return acc;
index 1a1008d..e96f40d 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
-import * as React from 'react';
+import React from 'react';
 import { RouteComponentProps, withRouter } from 'react-router-dom';
 
-import connect, { IDispatcher, Connect } from "../../../../framework/src/flux/connect";
-import { IApplicationStoreState } from "../../../../framework/src/store/applicationStore";
-import { MaterialTable, MaterialTableCtorType, ColumnType } from "../../../../framework/src/components/material-table";
-import { createConnectedNetworkElementsProperties, createConnectedNetworkElementsActions } from "../../../configurationApp/src/handlers/connectedNetworkElementsHandler";
+import { connect, IDispatcher, Connect } from '../../../../framework/src/flux/connect';
+import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
+import { MaterialTable, MaterialTableCtorType, ColumnType } from '../../../../framework/src/components/material-table';
 
-import { NetworkElementConnection } from "../models/networkElementConnection";
+import { NetworkElementConnection } from '../models/networkElementConnection';
+import { createConnectedNetworkElementsProperties, createConnectedNetworkElementsActions } from '../../../configurationApp/src/handlers/connectedNetworkElementsHandler';
 
 
 const mapProps = (state: IApplicationStoreState) => ({
@@ -47,20 +47,20 @@ class NetworkElementSelectorComponent extends React.Component<NetworkElementSele
 
     if (!initialSorted) {
       initialSorted = true;
-      this.props.connectedNetworkElementsActions.onHandleRequestSort("node-id");
+      this.props.connectedNetworkElementsActions.onHandleRequestSort('node-id');
     } else
       this.props.connectedNetworkElementsActions.onRefresh();
   }
 
   render() {
     return (
-      <ConnectedElementTable stickyHeader tableId="configurable-elements-table" onHandleClick={(e, row) => { this.props.history.push(`${this.props.match.path}/${row.nodeId}`) }} columns={[
-        { property: "nodeId", title: "Node 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 },
+      <ConnectedElementTable stickyHeader tableId="configurable-elements-table" onHandleClick={(e, row) => { this.props.history.push(`${this.props.match.path}/${row.nodeId}`); }} columns={[
+        { property: 'nodeId', title: 'Node 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>
     );
diff --git a/sdnr/wt/odlux/apps/configurationApp/src/yang/whenParser.ts b/sdnr/wt/odlux/apps/configurationApp/src/yang/whenParser.ts
new file mode 100644 (file)
index 0000000..fa2968c
--- /dev/null
@@ -0,0 +1,235 @@
+enum WhenTokenType {
+  AND = 'AND',
+  OR = 'OR',
+  NOT = 'NOT',
+  EQUALS = 'EQUALS',
+  COMMA = 'COMMA',
+  STRING = 'STRING',
+  FUNCTION = 'FUNCTION',
+  IDENTIFIER = 'IDENTIFIER',
+  OPEN_PAREN = 'OPEN_PAREN',
+  CLOSE_PAREN = 'CLOSE_PAREN',
+  EXPRESSION = 'EXPRESSION',
+}
+
+type Token = {
+  type: WhenTokenType;
+  value: string;
+};
+
+const isAlpha = (char: string) => /[a-z]/i.test(char);
+
+const isAlphaNumeric = (char: string) => /[A-Za-z0-9_\-/:\.]/i.test(char);
+
+const lex = (input: string) : Token[] => {
+  let tokens = [] as any[];
+  let current = 0;
+
+  while (current < input.length) {
+    let char = input[current];
+
+    if (char === ' ') {
+      current++;
+      continue;
+    }
+
+    if (char === '(') {
+      tokens.push({ type: WhenTokenType.OPEN_PAREN, value: char });
+      current++;
+      continue;
+    }
+
+    if (char === ')') {
+      tokens.push({ type: WhenTokenType.CLOSE_PAREN, value: char });
+      current++;
+      continue;
+    }
+
+    if (char === '=') {
+      tokens.push({ type: WhenTokenType.EQUALS, value: char });
+      current++;
+      continue;
+    }
+
+    if (char === ',') {
+      tokens.push({ type: WhenTokenType.COMMA, value: char });
+      current++;
+      continue;
+    }
+
+    if (char === '\"' || char === '\'') {
+      let value = '';
+      let start = current;
+      current++;
+
+      while (current < input.length) {
+        let innerChar = input[current];
+        if (innerChar === '\\') {
+          value += input[current] + input[current + 1];
+          current += 2;
+        } else if (innerChar === input[start]) {
+          current++;
+          break;
+        } else {
+          value += innerChar;
+          current++;
+        }
+      }
+
+      tokens.push({ type: WhenTokenType.STRING, value });
+      continue;
+    }
+
+    if (isAlpha(char)) {
+      let value = '';
+      while (isAlpha(char)) {
+        value += char;
+        char = input[++current];
+      }
+
+      switch (value) {
+        case 'and':
+          tokens.push({ type: WhenTokenType.AND });
+          break;
+        case 'or':
+          tokens.push({ type: WhenTokenType.OR });
+          break;
+        case 'not':
+          tokens.push({ type: WhenTokenType.NOT });
+          break;
+        case 'eq':
+          tokens.push({ type: WhenTokenType.EQUALS });
+          break;
+        default:
+          while (isAlphaNumeric(char)) {
+            value += char;
+            char = input[++current];
+          }
+          tokens.push({ type: WhenTokenType.IDENTIFIER, value });
+      }
+
+      continue;
+    }
+    if (isAlphaNumeric(char)) {
+      let value = '';
+      while (isAlphaNumeric(char)) {
+        value += char;
+        char = input[++current];
+      }
+
+      tokens.push({ type: WhenTokenType.IDENTIFIER, value });
+      continue;
+    }
+    throw new TypeError(`I don't know what this character is: ${char}`);
+  }
+  return tokens;
+};
+
+type WhenAST = {
+  type: WhenTokenType;
+  left?: WhenAST;
+  right?: WhenAST;
+  value?: string | WhenAST;
+  name?: string;
+  args?: WhenAST[];
+};
+
+const precedence : { [index: string] : number } = {
+  'EQUALS': 4,
+  'NOT': 3,
+  'AND': 2,
+  'OR': 1,
+};
+
+const parseWhen = (whenExpression: string) => {
+  const tokens = lex(whenExpression);
+  let current = 0;
+
+  const walk = (precedenceLevel = 0) : WhenAST => {
+    let token = tokens[current];
+    let node: WhenAST | null = null;
+
+    if (token.type === WhenTokenType.OPEN_PAREN) {
+      token = tokens[++current];
+      let innerNode: WhenAST = { type: WhenTokenType.EXPRESSION, value: walk() };
+      token = tokens[current];
+
+      while (token.type !== WhenTokenType.CLOSE_PAREN) {
+        innerNode = {
+          type: token.type,
+          value: token.value,
+          left: innerNode,
+          right: walk(),
+        };
+        token = tokens[current];
+      }
+      current++;
+      return innerNode;
+    }
+
+    if (token.type === WhenTokenType.STRING ) {
+      current++;
+      node = { type: token.type, value: token.value };
+    }
+
+    if (token.type === WhenTokenType.NOT) {
+      token = tokens[++current];
+      node = { type: WhenTokenType.NOT, value: token.value, right: walk() };
+    }
+
+    if (token.type === WhenTokenType.IDENTIFIER) {
+      const nextToken = tokens[current + 1];
+      if (nextToken.type === WhenTokenType.OPEN_PAREN) {
+        let name = token.value;
+        token = tokens[++current];
+
+        let args = [];
+        token = tokens[++current];
+
+        while (token.type !== WhenTokenType.CLOSE_PAREN) {
+          if (token.type === WhenTokenType.COMMA) {
+            current++;
+          } else {
+            args.push(walk());
+          }
+          token = tokens[current];
+        }
+
+        current++;
+        node = { type: WhenTokenType.FUNCTION, name, args };
+      } else {
+        current++;
+        node = { type: WhenTokenType.IDENTIFIER, value: token.value };
+      }
+    }   
+
+    if (!node) throw new TypeError('Unexpected token: ' + token.type);
+
+    token = tokens[current];
+    while (current < tokens.length && precedence[token.type] >= precedenceLevel) {
+      console.log(current, tokens[current], tokens[current].type, precedenceLevel, precedence[token.type]);
+      token = tokens[current];
+      if (token.type === WhenTokenType.EQUALS || token.type === WhenTokenType.AND || token.type === WhenTokenType.OR) {
+        current++;
+        node = {
+          type: token.type,
+          left: node,
+          right: walk(precedence[token.type]),
+        };
+      } else {
+        break;
+      }
+    }
+
+    return node;
+   
+  };
+
+  return walk();
+};
+
+export {
+  parseWhen,
+  WhenAST,
+  WhenTokenType,
+};
\ No newline at end of file
index e8e636f..cc25201 100644 (file)
@@ -1,3 +1,6 @@
+/* eslint-disable @typescript-eslint/no-loss-of-precision */
+/* eslint-disable @typescript-eslint/no-unused-expressions */
+/* eslint-disable @typescript-eslint/naming-convention */
 /**
  * ============LICENSE_START========================================================================
  * ONAP : ccsdk feature sdnr wt odlux
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import { Token, Statement, Module, Identity, ModuleState } from "../models/yang";
+import { Token, Statement, Module, Identity, ModuleState } from '../models/yang';
 import {
-  ViewSpecification, ViewElement, isViewElementObjectOrList, ViewElementBase,
-  isViewElementReference, ViewElementChoise, ViewElementBinary, ViewElementString, isViewElementString,
-  isViewElementNumber, ViewElementNumber, Expression, YangRange, ViewElementUnion, ViewElementRpc, isViewElementRpc, ResolveFunction, ViewElementDate
-} from "../models/uiModels";
-import { yangService } from "../services/yangService";
-
+  Expression,
+  ViewElement,
+  ViewElementBase,
+  ViewSpecification,
+  ViewElementNumber,
+  ViewElementString,
+  ViewElementChoice,
+  ViewElementUnion,
+  ViewElementRpc,
+  isViewElementObjectOrList,
+  isViewElementNumber,
+  isViewElementString,
+  isViewElementRpc,
+  ResolveFunction,
+  YangRange,
+} from '../models/uiModels';
+import { yangService } from '../services/yangService';
+
+const LOGLEVEL = +(localStorage.getItem('log.odlux.app.configuration.yang.yangParser') || 0);
+
+import { LogLevel } from '../../../../framework/src/utilities/logLevel';
+import { parseWhen, WhenAST, WhenTokenType } from './whenParser';
 
 export const splitVPath = (vPath: string, vPathParser: RegExp): RegExpMatchArray[] => {
   const pathParts: RegExpMatchArray[] = [];
@@ -32,21 +51,22 @@ export const splitVPath = (vPath: string, vPathParser: RegExp): RegExpMatchArray
     if (partMatch) {
       pathParts.push(partMatch);
     }
-  } while (partMatch)
+  } while (partMatch);
   return pathParts;
-}
+};
 
 class YangLexer {
 
   private pos: number = 0;
-  private buf: string = "";
+
+  private buf: string = '';
 
   constructor(input: string) {
     this.pos = 0;
     this.buf = input;
   }
 
-  private _optable: { [key: string]: string } = {
+  private _opTable: { [key: string]: string } = {
     ';': 'SEMI',
     '{': 'L_BRACE',
     '}': 'R_BRACE',
@@ -66,7 +86,7 @@ class YangLexer {
 
   private _isAlpha(char: string): boolean {
     return (char >= 'a' && char <= 'z') ||
-      (char >= 'A' && char <= 'Z')
+      (char >= 'A' && char <= 'Z');
   }
 
   private _isAlphanum(char: string): boolean {
@@ -74,7 +94,7 @@ class YangLexer {
       char === '_' || char === '-' || char === '.';
   }
 
-  private _skipNontokens() {
+  private _skipNonTokens() {
     while (this.pos < this.buf.length) {
       const char = this.buf.charAt(this.pos);
       if (this._isWhitespace(char)) {
@@ -90,11 +110,11 @@ class YangLexer {
     let end_index = this.pos + 1;
     while (end_index < this.buf.length) {
       const char = this.buf.charAt(end_index);
-      if (char === "\\") {
+      if (char === '\\') {
         end_index += 2;
         continue;
-      };
-      if (terminator === null && (this._isWhitespace(char) || this._optable[char] !== undefined) || char === terminator) {
+      }
+      if (terminator === null && (this._isWhitespace(char) || this._opTable[char] !== undefined) || char === terminator) {
         break;
       }
       end_index++;
@@ -109,7 +129,7 @@ class YangLexer {
         name: 'STRING',
         value: this.buf.substring(start, end),
         start,
-        end
+        end,
       };
       this.pos = terminator ? end + 1 : end;
       return tok;
@@ -122,8 +142,8 @@ class YangLexer {
       ++endpos;
     }
 
-    let name = 'IDENTIFIER'
-    if (this.buf.charAt(endpos) === ":") {
+    let name = 'IDENTIFIER';
+    if (this.buf.charAt(endpos) === ':') {
       name = 'IDENTIFIERREF';
       ++endpos;
       while (endpos < this.buf.length && this._isAlphanum(this.buf.charAt(endpos))) {
@@ -135,7 +155,7 @@ class YangLexer {
       name: name,
       value: this.buf.substring(this.pos, endpos),
       start: this.pos,
-      end: endpos
+      end: endpos,
     };
 
     this.pos = endpos;
@@ -153,7 +173,7 @@ class YangLexer {
       name: 'NUMBER',
       value: this.buf.substring(this.pos, endpos),
       start: this.pos,
-      end: endpos
+      end: endpos,
     };
     this.pos = endpos;
     return tok;
@@ -171,7 +191,7 @@ class YangLexer {
   private _processBlockComment() {
     var endpos = this.pos + 2;
     // Skip until the end of the line
-    while (endpos < this.buf.length && !((this.buf.charAt(endpos) === "/" && this.buf.charAt(endpos - 1) === "*"))) {
+    while (endpos < this.buf.length && !((this.buf.charAt(endpos) === '/' && this.buf.charAt(endpos - 1) === '*'))) {
       endpos++;
     }
     this.pos = endpos + 1;
@@ -179,87 +199,87 @@ class YangLexer {
 
   public tokenize(): Token[] {
     const result: Token[] = [];
-    this._skipNontokens();
+    this._skipNonTokens();
     while (this.pos < this.buf.length) {
 
       const char = this.buf.charAt(this.pos);
-      const op = this._optable[char];
+      const op = this._opTable[char];
 
       if (op !== undefined) {
         result.push({ name: op, value: char, start: this.pos, end: ++this.pos });
       } else if (this._isAlpha(char)) {
         result.push(this._processIdentifier());
-        this._skipNontokens();
+        this._skipNonTokens();
         const peekChar = this.buf.charAt(this.pos);
-        if (this._optable[peekChar] === undefined) {
-          result.push((peekChar !== "'" && peekChar !== '"')
+        if (this._opTable[peekChar] === undefined) {
+          result.push((peekChar !== '\'' && peekChar !== '"')
             ? this._processString(null)
             : this._processString(peekChar));
         }
-      } else if (char === '/' && this.buf.charAt(this.pos + 1) === "/") {
+      } else if (char === '/' && this.buf.charAt(this.pos + 1) === '/') {
         this._processLineComment();
-      } else if (char === '/' && this.buf.charAt(this.pos + 1) === "*") {
+      } else if (char === '/' && this.buf.charAt(this.pos + 1) === '*') {
         this._processBlockComment();
       } else {
-        throw Error('Token error at ' + this.pos + " " + this.buf[this.pos]);
+        throw Error('Token error at ' + this.pos + ' ' + this.buf[this.pos]);
       }
-      this._skipNontokens();
+      this._skipNonTokens();
     }
     return result;
   }
 
   public tokenize2(): Statement {
-    let stack: Statement[] = [{ key: "ROOT", sub: [] }];
+    let stack: Statement[] = [{ key: 'ROOT', sub: [] }];
     let current: Statement | null = null;
 
-    this._skipNontokens();
+    this._skipNonTokens();
     while (this.pos < this.buf.length) {
 
       const char = this.buf.charAt(this.pos);
-      const op = this._optable[char];
+      const op = this._opTable[char];
 
       if (op !== undefined) {
-        if (op === "L_BRACE") {
+        if (op === 'L_BRACE') {
           current && stack.unshift(current);
           current = null;
-        } else if (op === "R_BRACE") {
+        } else if (op === 'R_BRACE') {
           current = stack.shift() || null;
         }
         this.pos++;
-      } else if (this._isAlpha(char) || char === "_") {
+      } else if (this._isAlpha(char) || char === '_') {
         const key = this._processIdentifier().value;
-        this._skipNontokens();
+        this._skipNonTokens();
         let peekChar = this.buf.charAt(this.pos);
         let arg = undefined;
-        if (this._optable[peekChar] === undefined) {
-          arg = (peekChar === '"' || peekChar === "'")
+        if (this._opTable[peekChar] === undefined) {
+          arg = (peekChar === '"' || peekChar === '\'')
             ? this._processString(peekChar).value
             : this._processString(null).value;
         }
         do {
-          this._skipNontokens();
+          this._skipNonTokens();
           peekChar = this.buf.charAt(this.pos);
-          if (peekChar !== "+") break;
+          if (peekChar !== '+') break;
           this.pos++;
-          this._skipNontokens();
+          this._skipNonTokens();
           peekChar = this.buf.charAt(this.pos);
-          arg += (peekChar === '"' || peekChar === "'")
+          arg += (peekChar === '"' || peekChar === '\'')
             ? this._processString(peekChar).value
             : this._processString(null).value;
         } while (true);
         current = { key, arg, sub: [] };
         stack[0].sub!.push(current);
-      } else if (char === '/' && this.buf.charAt(this.pos + 1) === "/") {
+      } else if (char === '/' && this.buf.charAt(this.pos + 1) === '/') {
         this._processLineComment();
-      } else if (char === '/' && this.buf.charAt(this.pos + 1) === "*") {
+      } else if (char === '/' && this.buf.charAt(this.pos + 1) === '*') {
         this._processBlockComment();
       } else {
-        throw Error('Token error at ' + this.pos + " " + this.buf.slice(this.pos - 10, this.pos + 10));
+        throw Error('Token error at ' + this.pos + ' ' + this.buf.slice(this.pos - 10, this.pos + 10));
       }
-      this._skipNontokens();
+      this._skipNonTokens();
     }
-    if (stack[0].key !== "ROOT" || !stack[0].sub![0]) {
-      throw new Error("Internal Perser Error");
+    if (stack[0].key !== 'ROOT' || !stack[0].sub![0]) {
+      throw new Error('Internal Perser Error');
     }
     return stack[0].sub![0];
   }
@@ -269,25 +289,33 @@ export class YangParser {
   private _groupingsToResolve: ViewSpecification[] = [];
 
   private _identityToResolve: (() => void)[] = [];
+
   private _unionsToResolve: (() => void)[] = [];
+
   private _modulesToResolve: (() => void)[] = [];
 
   private _modules: { [name: string]: Module } = {};
+
   private _views: ViewSpecification[] = [{
-    id: "0",
-    name: "root",
-    language: "en-US",
+    id: '0',
+    name: 'root',
+    language: 'en-US',
     canEdit: false,
     config: true,
-    parentView: "0",
-    title: "root",
+    parentView: '0',
+    title: 'root',
     elements: {},
   }];
 
-  public static ResolveStack = Symbol("ResolveStack");
+  public static ResolveStack = Symbol('ResolveStack');
+
+  constructor(
+    private nodeId: string,
+    private _capabilityRevisionMap: { [capability: string]: string } = {},
+    private _unavailableCapabilities: { failureReason: string; capability: string }[] = [],
+    private _importOnlyModules: { name: string; revision: string }[] = [],
+  ) {
 
-  constructor(private _unavailableCapabilities: { failureReason: string; capability: string; }[] = [], private _importOnlyModules: { name: string; revision: string; }[] = [], private nodeId: string) {
-   
   }
 
   public get modules() {
@@ -300,8 +328,12 @@ export class YangParser {
 
   public async addCapability(capability: string, version?: string, parentImportOnlyModule?: boolean) {
     // do not add twice
-    if (this._modules[capability]) {
-      // console.warn(`Skipped capability: ${capability} since already contained.` );
+    const existingCapability = this._modules[capability];
+    const latestVersionExisting = existingCapability && Object.keys(existingCapability.revisions).sort().reverse()[0];
+    if ((latestVersionExisting && version) && (version <= latestVersionExisting)) {
+      if (LOGLEVEL == LogLevel.Warning) {
+        console.warn(`Skipped capability: ${capability}:${version || ''} since already contained.`);
+      }
       return;
     }
 
@@ -310,14 +342,15 @@ export class YangParser {
     //   // console.warn(`Skipped capability: ${capability} since it is marked as unavailable.` );
     //   return;
     // }
+
     const data = await yangService.getCapability(capability, this.nodeId, version);
     if (!data) {
-      throw new Error(`Could not load yang file for ${capability}.`);
+      throw new Error(`Could not load yang file for ${capability}:${version || ''}.`);
     }
 
     const rootStatement = new YangLexer(data).tokenize2();
 
-    if (rootStatement.key !== "module") {
+    if (rootStatement.key !== 'module') {
       throw new Error(`Root element of ${capability} is not a module.`);
     }
     if (rootStatement.arg !== capability) {
@@ -326,10 +359,32 @@ export class YangParser {
 
     const isUnavailable = this._unavailableCapabilities.some(c => c.capability === capability);
     const isImportOnly = parentImportOnlyModule === true || this._importOnlyModules.some(c => c.name === capability);
+
+    // extract revisions
+    const revisions = this.extractNodes(rootStatement, 'revision').reduce<{ [version: string]: {} }>((acc, revision) => {
+      if (!revision.arg) {
+        throw new Error(`Module [${rootStatement.arg}] has a version w/o version number.`);
+      }
+      const description = this.extractValue(revision, 'description');
+      const reference = this.extractValue(revision, 'reference');
+      acc[revision.arg] = {
+        description,
+        reference,
+      };
+      return acc;
+    }, {});
+
+    const latestVersionLoaded = Object.keys(revisions).sort().reverse()[0];
+    if (existingCapability && latestVersionExisting >= latestVersionLoaded) {
+      if (LOGLEVEL == LogLevel.Warning) {
+        console.warn(`Skipped capability: ${capability}:${latestVersionLoaded} since ${capability}:${latestVersionExisting} already contained.`);
+      }
+      return;
+    }
     
     const module = this._modules[capability] = {
       name: rootStatement.arg,
-      revisions: {},
+      revisions,
       imports: {},
       features: {},
       identities: {},
@@ -339,10 +394,10 @@ export class YangParser {
       views: {},
       elements: {},
       state: isUnavailable
-           ? ModuleState.unavailable 
-           : isImportOnly 
-             ? ModuleState.importOnly
-             : ModuleState.stable,
+        ? ModuleState.unavailable
+        : isImportOnly
+          ? ModuleState.importOnly
+          : ModuleState.stable,
     };
 
     await this.handleModule(module, rootStatement, capability);
@@ -351,84 +406,66 @@ export class YangParser {
   private async handleModule(module: Module, rootStatement: Statement, capability: string) {
 
     // extract namespace && prefix
-    module.namespace = this.extractValue(rootStatement, "namespace");
-    module.prefix = this.extractValue(rootStatement, "prefix");
+    module.namespace = this.extractValue(rootStatement, 'namespace');
+    module.prefix = this.extractValue(rootStatement, 'prefix');
     if (module.prefix) {
       module.imports[module.prefix] = capability;
     }
 
-    // extract revisions
-    const revisions = this.extractNodes(rootStatement, "revision");
-    module.revisions = {
-      ...module.revisions,
-      ...revisions.reduce<{ [version: string]: {} }>((acc, version) => {
-        if (!version.arg) {
-          throw new Error(`Module [${module.name}] has a version w/o version number.`);
-        }
-        const description = this.extractValue(version, "description");
-        const reference = this.extractValue(version, "reference");
-        acc[version.arg] = {
-          description,
-          reference,
-        };
-        return acc;
-      }, {})
-    };
-
     // extract features
-    const features = this.extractNodes(rootStatement, "feature");
+    const features = this.extractNodes(rootStatement, 'feature');
     module.features = {
       ...module.features,
       ...features.reduce<{ [version: string]: {} }>((acc, feature) => {
         if (!feature.arg) {
           throw new Error(`Module [${module.name}] has a feature w/o name.`);
         }
-        const description = this.extractValue(feature, "description");
+        const description = this.extractValue(feature, 'description');
         acc[feature.arg] = {
           description,
         };
         return acc;
-      }, {})
+      }, {}),
     };
 
     // extract imports
-    const imports = this.extractNodes(rootStatement, "import");
+    const imports = this.extractNodes(rootStatement, 'import');
     module.imports = {
       ...module.imports,
       ...imports.reduce<{ [key: string]: string }>((acc, imp) => {
-        const prefix = imp.sub && imp.sub.filter(s => s.key === "prefix");
+        const prefix = imp.sub && imp.sub.filter(s => s.key === 'prefix');
         if (!imp.arg) {
           throw new Error(`Module [${module.name}] has an import with neither name nor prefix.`);
         }
         acc[prefix && prefix.length === 1 && prefix[0].arg || imp.arg] = imp.arg;
         return acc;
-      }, {})
+      }, {}),
     };
 
     // import all required files and set module state 
     if (imports) for (let ind = 0; ind < imports.length; ++ind) {
-      const moduleName = imports[ind].arg!; 
+      const moduleName = imports[ind].arg!;
 
-      //TODO: Fix imports getting loaded without revision
-      await this.addCapability(moduleName, undefined, module.state === ModuleState.importOnly);
+      const revision = this._capabilityRevisionMap[moduleName] || undefined;
+      await this.addCapability(moduleName, revision, module.state === ModuleState.importOnly);
       const importedModule = this._modules[imports[ind].arg!];
       if (importedModule && importedModule.state > ModuleState.stable) {
-          module.state = Math.max(module.state, ModuleState.instable);
+        module.state = Math.max(module.state, ModuleState.instable);
       }
     }
 
-    this.extractTypeDefinitions(rootStatement, module, "");
+    this.extractTypeDefinitions(rootStatement, module, '');
 
-    this.extractIdentities(rootStatement, 0, module, "");
+    this.extractIdentities(rootStatement, 0, module, '');
 
-    const groupings = this.extractGroupings(rootStatement, 0, module, "");
+    const groupings = this.extractGroupings(rootStatement, 0, module, '');
     this._views.push(...groupings);
 
-    const augments = this.extractAugments(rootStatement, 0, module, "");
+    const augments = this.extractAugments(rootStatement, 0, module, '');
     this._views.push(...augments);
 
     // the default for config on module level is config = true;
-    const [currentView, subViews] = this.extractSubViews(rootStatement, 0, module, "");
+    const [currentView, subViews] = this.extractSubViews(rootStatement, 0, module, '');
     this._views.push(currentView, ...subViews);
 
     // create the root elements for this module
@@ -443,7 +480,7 @@ export class YangParser {
           const viewIdIndex = Number(viewElement.viewId);
           module.views[key] = this._views[viewIdIndex];
         }
-        
+
         // add only the UI View if the module is available
         if (module.state === ModuleState.stable || module.state === ModuleState.instable) this._views[0].elements[key] = module.elements[key];
       });
@@ -462,7 +499,7 @@ export class YangParser {
 
     // process all groupings
     this._groupingsToResolve.filter(vs => vs.uses && vs.uses[ResolveFunction]).forEach(vs => {
-      try { vs.uses![ResolveFunction] !== undefined && vs.uses![ResolveFunction]!("|"); } catch (error) {
+      try { vs.uses![ResolveFunction] !== undefined && vs.uses![ResolveFunction]!('|'); } catch (error) {
         console.warn(`Error resolving: [${vs.name}] [${error.message}]`);
       }
     });
@@ -471,16 +508,16 @@ export class YangParser {
      * This is to fix the issue for sequential execution of modules based on their child and parent relationship
      * We are sorting the module object based on their augment status
      */
-      Object.keys(this.modules)
+    Object.keys(this.modules)
       .map(elem => {
-          if(this.modules[elem].augments && Object.keys(this.modules[elem].augments).length > 0) {
-              const {augments, ...rest} = this.modules[elem];
-              const partsOfKeys = Object.keys(augments).map((key) => (key.split("/").length - 1))
-              this.modules[elem].executionOrder= Math.max(...partsOfKeys)
-          } else {
-            this.modules[elem].executionOrder=0;
-          }
-      })
+        if (this.modules[elem].augments && Object.keys(this.modules[elem].augments).length > 0) {
+          const { augments, ..._rest } = this.modules[elem];
+          const partsOfKeys = Object.keys(augments).map((key) => (key.split('/').length - 1));
+          this.modules[elem].executionOrder = Math.max(...partsOfKeys);
+        } else {
+          this.modules[elem].executionOrder = 0;
+        }
+      });
 
     // process all augmentations / sort by namespace changes to ensure proper order 
     Object.keys(this.modules).sort((a, b) => this.modules[a].executionOrder! - this.modules[b].executionOrder!).forEach(modKey => {
@@ -489,8 +526,8 @@ export class YangParser {
         const pathParts = splitVPath(key, /(?:(?:([^\/\:]+):)?([^\/]+))/g);  // 1 = opt: namespace / 2 = property 
         let nameSpaceChangeCounter = 0;
         let currentNS = module.name; // init namespace
-        pathParts.forEach(([ns, _])=> {
-          if (ns === currentNS){
+        pathParts.forEach(([ns, _]) => {
+          if (ns === currentNS) {
             currentNS = ns;
             nameSpaceChangeCounter++;
           }
@@ -498,11 +535,11 @@ export class YangParser {
         return {
           key,
           nameSpaceChangeCounter,
-        }
+        };
       });
-      
+
       const augmentKeys = augmentKeysWithCounter
-        .sort((a,b) => a.nameSpaceChangeCounter > b.nameSpaceChangeCounter ? 1 : a.nameSpaceChangeCounter === b.nameSpaceChangeCounter ? 0 : -1 )
+        .sort((a, b) => a.nameSpaceChangeCounter > b.nameSpaceChangeCounter ? 1 : a.nameSpaceChangeCounter === b.nameSpaceChangeCounter ? 0 : -1)
         .map((a) => a.key);
 
       augmentKeys.forEach(augKey => {
@@ -512,11 +549,23 @@ export class YangParser {
         if (augments && viewSpec) {
           augments.forEach(augment => Object.keys(augment.elements).forEach(key => {
             const elm = augment.elements[key];
+            
+            const when = elm.when && augment.when
+              ? {
+                type: WhenTokenType.AND,
+                left: elm.when,
+                right: augment.when,
+              }
+              : elm.when || augment.when;
+            
+            const ifFeature = elm.ifFeature
+              ? `(${augment.ifFeature}) and (${elm.ifFeature})`
+              : augment.ifFeature;
+            
             viewSpec.elements[key] = {
               ...augment.elements[key],
-              
-              when: elm.when ? `(${augment.when}) and (${elm.when})` : augment.when,
-              ifFeature: elm.ifFeature ? `(${augment.ifFeature}) and (${elm.ifFeature})` : augment.ifFeature,
+              when,
+              ifFeature,
             };
           }));
         }
@@ -534,7 +583,7 @@ export class YangParser {
         }
       }
       return result;
-    }
+    };
 
     const baseIdentities: Identity[] = [];
     Object.keys(this.modules).forEach(modKey => {
@@ -565,30 +614,31 @@ export class YangParser {
       }
     });
 
-    // resolve readOnly
-    const resolveReadOnly = (view: ViewSpecification, parentConfig: boolean) => {
-      
-      // update view config
-      view.config = view.config && parentConfig;
-      
-      Object.keys(view.elements).forEach((key) => {
-        const elm = view.elements[key];
-
-        // update element config
-        elm.config = elm.config && view.config;
-        
-        // update all sub-elements of objects
-        if (elm.uiType === "object") {
-          resolveReadOnly(this.views[+elm.viewId], elm.config);
-        }
+    // // resolve readOnly
+    // const resolveReadOnly = (view: ViewSpecification, parentConfig: boolean) => {
 
-      })
-    }
+    //   // update view config
+    //   view.config = view.config && parentConfig;
 
-    const dump = resolveReadOnly(this.views[0], true); 
-  };
+    //   Object.keys(view.elements).forEach((key) => {
+    //     const elm = view.elements[key];
+
+    //     // update element config
+    //     elm.config = elm.config && view.config;
+
+    //     // update all sub-elements of objects
+    //     if (elm.uiType === 'object') {
+    //       resolveReadOnly(this.views[+elm.viewId], elm.config);
+    //     }
+
+    //   });
+    // };
+
+    // const dump = resolveReadOnly(this.views[0], true);
+  }
 
   private _nextId = 1;
+
   private get nextId() {
     return this._nextId++;
   }
@@ -608,7 +658,7 @@ export class YangParser {
   }
 
   private extractTypeDefinitions(statement: Statement, module: Module, currentPath: string): void {
-    const typedefs = this.extractNodes(statement, "typedef");
+    const typedefs = this.extractNodes(statement, 'typedef');
     typedefs && typedefs.forEach(def => {
       if (!def.arg) {
         throw new Error(`Module: [${module.name}]. Found typefed without name.`);
@@ -620,7 +670,7 @@ export class YangParser {
   /** Handles groupings like named Container */
   private extractGroupings(statement: Statement, parentId: number, module: Module, currentPath: string): ViewSpecification[] {
     const subViews: ViewSpecification[] = [];
-    const groupings = this.extractNodes(statement, "grouping");
+    const groupings = this.extractNodes(statement, 'grouping');
     if (groupings && groupings.length > 0) {
       subViews.push(...groupings.reduce<ViewSpecification[]>((acc, cur) => {
         if (!cur.arg) {
@@ -629,9 +679,9 @@ export class YangParser {
         const grouping = cur.arg;
 
         // the default for config on module level is config = true;
-        const [currentView, subViews] = this.extractSubViews(cur, /* parentId */ -1, module, currentPath);
+        const [currentView, currentSubViews] = this.extractSubViews(cur, /* parentId */ -1, module, currentPath);
         grouping && (module.groupings[grouping] = currentView);
-        acc.push(currentView, ...subViews);
+        acc.push(currentView, ...currentSubViews);
         return acc;
       }, []));
     }
@@ -642,7 +692,7 @@ export class YangParser {
   /** Handles augments also like named container */
   private extractAugments(statement: Statement, parentId: number, module: Module, currentPath: string): ViewSpecification[] {
     const subViews: ViewSpecification[] = [];
-    const augments = this.extractNodes(statement, "augment");
+    const augments = this.extractNodes(statement, 'augment');
     if (augments && augments.length > 0) {
       subViews.push(...augments.reduce<ViewSpecification[]>((acc, cur) => {
         if (!cur.arg) {
@@ -651,12 +701,12 @@ export class YangParser {
         const augment = this.resolveReferencePath(cur.arg, module);
 
         // the default for config on module level is config = true;
-        const [currentView, subViews] = this.extractSubViews(cur, parentId, module, currentPath);
+        const [currentView, currentSubViews] = this.extractSubViews(cur, parentId, module, currentPath);
         if (augment) {
           module.augments[augment] = module.augments[augment] || [];
           module.augments[augment].push(currentView);
         }
-        acc.push(currentView, ...subViews);
+        acc.push(currentView, ...currentSubViews);
         return acc;
       }, []));
     }
@@ -666,109 +716,109 @@ export class YangParser {
 
   /** Handles identities  */
   private extractIdentities(statement: Statement, parentId: number, module: Module, currentPath: string) {
-    const identities = this.extractNodes(statement, "identity");
+    const identities = this.extractNodes(statement, 'identity');
     module.identities = identities.reduce<{ [name: string]: Identity }>((acc, cur) => {
       if (!cur.arg) {
-        throw new Error(`Module: [${module.name}][${currentPath}]. Found identiy without name.`);
+        throw new Error(`Module: [${module.name}][${currentPath}]. Found identity without name.`);
       }
       acc[cur.arg] = {
         id: `${module.name}:${cur.arg}`,
         label: cur.arg,
-        base: this.extractValue(cur, "base"),
-        description: this.extractValue(cur, "description"),
-        reference: this.extractValue(cur, "reference"),
-        children: []
-      }
+        base: this.extractValue(cur, 'base'),
+        description: this.extractValue(cur, 'description'),
+        reference: this.extractValue(cur, 'reference'),
+        children: [],
+      };
       return acc;
     }, {});
   }
 
-   // Hint: use 0 as parentId for rootElements and -1 for rootGroupings.
+  // Hint: use 0 as parentId for rootElements and -1 for rootGroupings.
   private extractSubViews(statement: Statement, parentId: number, module: Module, currentPath: string): [ViewSpecification, ViewSpecification[]] {
     // used for scoped definitions
     const context: Module = {
       ...module,
       typedefs: {
-        ...module.typedefs
-      }
+        ...module.typedefs,
+      },
     };
 
     const currentId = this.nextId;
     const subViews: ViewSpecification[] = [];
     let elements: ViewElement[] = [];
 
-    const configValue = this.extractValue(statement, "config");
-    const config = configValue == null ? true : configValue.toLocaleLowerCase() !== "false";
+    const configValue = this.extractValue(statement, 'config');
+    const config = configValue == null ? true : configValue.toLocaleLowerCase() !== 'false';
 
     // extract conditions
-    const ifFeature = this.extractValue(statement, "if-feature");
-    const whenCondition = this.extractValue(statement, "when");
-    if (whenCondition) console.warn("Found in [" + context.name + "]" + currentPath + " when: " + whenCondition);
+    const ifFeature = this.extractValue(statement, 'if-feature');
+    const whenCondition = this.extractValue(statement, 'when');
+    if (whenCondition) console.warn('Found in [' + context.name + ']' + currentPath + ' when: ' + whenCondition);
 
     // extract all scoped typedefs
     this.extractTypeDefinitions(statement, context, currentPath);
 
     // extract all scoped groupings
     subViews.push(
-      ...this.extractGroupings(statement, parentId, context, currentPath)
+      ...this.extractGroupings(statement, parentId, context, currentPath),
     );
 
     // extract all container
-    const container = this.extractNodes(statement, "container");
+    const container = this.extractNodes(statement, 'container');
     if (container && container.length > 0) {
       subViews.push(...container.reduce<ViewSpecification[]>((acc, cur) => {
         if (!cur.arg) {
           throw new Error(`Module: [${context.name}]${currentPath}. Found container without name.`);
         }
-        const [currentView, subViews] = this.extractSubViews(cur, currentId, context, `${currentPath}/${context.name}:${cur.arg}`);
+        const [currentView, currentSubViews] = this.extractSubViews(cur, currentId, context, `${currentPath}/${context.name}:${cur.arg}`);
         elements.push({
           id: parentId === 0 ? `${context.name}:${cur.arg}` : cur.arg,
           label: cur.arg,
           path: currentPath,
           module: context.name || module.name || '',
-          uiType: "object",
+          uiType: 'object',
           viewId: currentView.id,
           config: currentView.config,
         });
-        acc.push(currentView, ...subViews);
+        acc.push(currentView, ...currentSubViews);
         return acc;
       }, []));
     }
 
     // process all lists
     // a list is a list of containers with the leafs contained in the list
-    const lists = this.extractNodes(statement, "list");
+    const lists = this.extractNodes(statement, 'list');
     if (lists && lists.length > 0) {
       subViews.push(...lists.reduce<ViewSpecification[]>((acc, cur) => {
         let elmConfig = config;
         if (!cur.arg) {
           throw new Error(`Module: [${context.name}]${currentPath}. Found list without name.`);
         }
-        const key = this.extractValue(cur, "key") || undefined;
+        const key = this.extractValue(cur, 'key') || undefined;
         if (elmConfig && !key) {
           console.warn(`Module: [${context.name}]${currentPath}. Found configurable list without key. Assume config shell be false.`);
           elmConfig = false;
         }
-        const [currentView, subViews] = this.extractSubViews(cur, currentId, context, `${currentPath}/${context.name}:${cur.arg}`);
+        const [currentView, currentSubViews] = this.extractSubViews(cur, currentId, context, `${currentPath}/${context.name}:${cur.arg}`);
         elements.push({
           id: parentId === 0 ? `${context.name}:${cur.arg}` : cur.arg,
           label: cur.arg,
           path: currentPath,
           module: context.name || module.name || '',
           isList: true,
-          uiType: "object",
+          uiType: 'object',
           viewId: currentView.id,
           key: key,
           config: elmConfig && currentView.config,
         });
-        acc.push(currentView, ...subViews);
+        acc.push(currentView, ...currentSubViews);
         return acc;
       }, []));
     }
 
     // process all leaf-lists
     // a leaf-list is a list of some type
-    const leafLists = this.extractNodes(statement, "leaf-list");
+    const leafLists = this.extractNodes(statement, 'leaf-list');
     if (leafLists && leafLists.length > 0) {
       elements.push(...leafLists.reduce<ViewElement[]>((acc, cur) => {
         const element = this.getViewElement(cur, context, parentId, currentPath, true);
@@ -779,7 +829,7 @@ export class YangParser {
 
     // process all leafs
     // a leaf is mainly a property of an object
-    const leafs = this.extractNodes(statement, "leaf");
+    const leafs = this.extractNodes(statement, 'leaf');
     if (leafs && leafs.length > 0) {
       elements.push(...leafs.reduce<ViewElement[]>((acc, cur) => {
         const element = this.getViewElement(cur, context, parentId, currentPath, false);
@@ -789,92 +839,92 @@ export class YangParser {
     }
 
 
-    const choiceStms = this.extractNodes(statement, "choice");
+    const choiceStms = this.extractNodes(statement, 'choice');
     if (choiceStms && choiceStms.length > 0) {
-      elements.push(...choiceStms.reduce<ViewElementChoise[]>((accChoise, curChoise) => {
-        if (!curChoise.arg) {
+      elements.push(...choiceStms.reduce<ViewElementChoice[]>((accChoice, curChoice) => {
+        if (!curChoice.arg) {
           throw new Error(`Module: [${context.name}]${currentPath}. Found choise without name.`);
         }
         // extract all cases like containers
-        const cases: { id: string, label: string, description?: string, elements: { [name: string]: ViewElement } }[] = [];
-        const caseStms = this.extractNodes(curChoise, "case");
+        const cases: { id: string; label: string; description?: string; elements: { [name: string]: ViewElement } }[] = [];
+        const caseStms = this.extractNodes(curChoice, 'case');
         if (caseStms && caseStms.length > 0) {
           cases.push(...caseStms.reduce((accCase, curCase) => {
             if (!curCase.arg) {
-              throw new Error(`Module: [${context.name}]${currentPath}/${curChoise.arg}. Found case without name.`);
+              throw new Error(`Module: [${context.name}]${currentPath}/${curChoice.arg}. Found case without name.`);
             }
-            const description = this.extractValue(curCase, "description") || undefined;
-            const [caseView, caseSubViews] = this.extractSubViews(curCase, parentId, context, `${currentPath}/${context.name}:${curChoise.arg}`);
+            const description = this.extractValue(curCase, 'description') || undefined;
+            const [caseView, caseSubViews] = this.extractSubViews(curCase, parentId, context, `${currentPath}/${context.name}:${curChoice.arg}`);
             subViews.push(caseView, ...caseSubViews);
 
-            const caseDef: { id: string, label: string, description?: string, elements: { [name: string]: ViewElement } } = {
+            const caseDef: { id: string; label: string; description?: string; elements: { [name: string]: ViewElement } } = {
               id: parentId === 0 ? `${context.name}:${curCase.arg}` : curCase.arg,
               label: curCase.arg,
               description: description,
-              elements: caseView.elements
+              elements: caseView.elements,
             };
             accCase.push(caseDef);
             return accCase;
-          }, [] as { id: string, label: string, description?: string, elements: { [name: string]: ViewElement } }[]));
+          }, [] as { id: string; label: string; description?: string; elements: { [name: string]: ViewElement } }[]));
         }
 
         // extract all simple cases (one case per leaf, container, etc.)
-        const [choiseView, choiseSubViews] = this.extractSubViews(curChoise, parentId, context, `${currentPath}/${context.name}:${curChoise.arg}`);
-        subViews.push(choiseView, ...choiseSubViews);
-        cases.push(...Object.keys(choiseView.elements).reduce((accElm, curElm) => {
-          const elm = choiseView.elements[curElm];
-          const caseDef: { id: string, label: string, description?: string, elements: { [name: string]: ViewElement } } = {
+        const [choiceView, choiceSubViews] = this.extractSubViews(curChoice, parentId, context, `${currentPath}/${context.name}:${curChoice.arg}`);
+        subViews.push(choiceView, ...choiceSubViews);
+        cases.push(...Object.keys(choiceView.elements).reduce((accElm, curElm) => {
+          const elm = choiceView.elements[curElm];
+          const caseDef: { id: string; label: string; description?: string; elements: { [name: string]: ViewElement } } = {
             id: elm.id,
             label: elm.label,
             description: elm.description,
-            elements: { [elm.id]: elm }
+            elements: { [elm.id]: elm },
           };
           accElm.push(caseDef);
           return accElm;
-        }, [] as { id: string, label: string, description?: string, elements: { [name: string]: ViewElement } }[]));
+        }, [] as { id: string; label: string; description?: string; elements: { [name: string]: ViewElement } }[]));
 
-        const description = this.extractValue(curChoise, "description") || undefined;
-        const configValue = this.extractValue(curChoise, "config");
-        const config = configValue == null ? true : configValue.toLocaleLowerCase() !== "false";
+        const choiceDescription = this.extractValue(curChoice, 'description') || undefined;
+        const choiceConfigValue = this.extractValue(curChoice, 'config');
+        const choiceConfig = choiceConfigValue == null ? true : choiceConfigValue.toLocaleLowerCase() !== 'false';
 
-        const mandatory = this.extractValue(curChoise, "mandatory") === "true" || false;
+        const mandatory = this.extractValue(curChoice, 'mandatory') === 'true' || false;
 
-        const element: ViewElementChoise = {
-          uiType: "choise",
-          id: parentId === 0 ? `${context.name}:${curChoise.arg}` : curChoise.arg,
-          label: curChoise.arg,
+        const element: ViewElementChoice = {
+          uiType: 'choice',
+          id: parentId === 0 ? `${context.name}:${curChoice.arg}` : curChoice.arg,
+          label: curChoice.arg,
           path: currentPath,
           module: context.name || module.name || '',
-          config: config,
+          config: choiceConfig,
           mandatory: mandatory,
-          description: description,
+          description: choiceDescription,
           cases: cases.reduce((acc, cur) => {
             acc[cur.id] = cur;
             return acc;
-          }, {} as { [name: string]: { id: string, label: string, description?: string, elements: { [name: string]: ViewElement } } })
+          }, {} as { [name: string]: { id: string; label: string; description?: string; elements: { [name: string]: ViewElement } } }),
         };
 
-        accChoise.push(element);
-        return accChoise;
+        accChoice.push(element);
+        return accChoice;
       }, []));
     }
 
-    const rpcStms = this.extractNodes(statement, "rpc");
+    const rpcStms = this.extractNodes(statement, 'rpc');
     if (rpcStms && rpcStms.length > 0) {
       elements.push(...rpcStms.reduce<ViewElementRpc[]>((accRpc, curRpc) => {
         if (!curRpc.arg) {
           throw new Error(`Module: [${context.name}]${currentPath}. Found rpc without name.`);
         }
 
-        const description = this.extractValue(curRpc, "description") || undefined;
-        const configValue = this.extractValue(curRpc, "config");
-        const config = configValue == null ? true : configValue.toLocaleLowerCase() !== "false";
+        const rpcDescription = this.extractValue(curRpc, 'description') || undefined;
+        const rpcConfigValue = this.extractValue(curRpc, 'config');
+        const rpcConfig = rpcConfigValue == null ? true : rpcConfigValue.toLocaleLowerCase() !== 'false';
 
         let inputViewId: string | undefined = undefined;
         let outputViewId: string | undefined = undefined;
 
-        const input = this.extractNodes(curRpc, "input") || undefined;
-        const output = this.extractNodes(curRpc, "output") || undefined;
+        const input = this.extractNodes(curRpc, 'input') || undefined;
+        const output = this.extractNodes(curRpc, 'output') || undefined;
 
         if (input && input.length > 0) {
           const [inputView, inputSubViews] = this.extractSubViews(input[0], parentId, context, `${currentPath}/${context.name}:${curRpc.arg}`);
@@ -889,13 +939,13 @@ export class YangParser {
         }
 
         const element: ViewElementRpc = {
-          uiType: "rpc",
+          uiType: 'rpc',
           id: parentId === 0 ? `${context.name}:${curRpc.arg}` : curRpc.arg,
           label: curRpc.arg,
           path: currentPath,
           module: context.name || module.name || '',
-          config: config,
-          description: description,
+          config: rpcConfig,
+          description: rpcDescription,
           inputViewId: inputViewId,
           outputViewId: outputViewId,
         };
@@ -906,9 +956,16 @@ export class YangParser {
       }, []));
     }
 
-    // if (!statement.arg) {
-    //   throw new Error(`Module: [${context.name}]. Found statement without name.`);
-    // }
+    if (!statement.arg) {
+      console.error(new Error(`Module: [${context.name}]. Found statement without name.`));
+    }
+
+    let whenParsed: WhenAST | undefined = undefined;
+    try {
+      whenParsed = whenCondition && parseWhen(whenCondition) || undefined;
+    } catch (e) {
+      console.error(new Error(`Module: [${context.name}]. Found invalid when condition: ${whenCondition}`));
+    }
 
     const viewSpec: ViewSpecification = {
       id: String(currentId),
@@ -916,11 +973,11 @@ export class YangParser {
       ns: context.name,
       name: statement.arg != null ? statement.arg : undefined,
       title: statement.arg != null ? statement.arg : undefined,
-      language: "en-us",
+      language: 'en-us',
       canEdit: false,
       config: config,
       ifFeature: ifFeature,
-      when: whenCondition,
+      when: whenParsed,
       elements: elements.reduce<{ [name: string]: ViewElement }>((acc, cur) => {
         acc[cur.id] = cur;
         return acc;
@@ -928,21 +985,21 @@ export class YangParser {
     };
 
     // evaluate canEdit depending on all conditions
-    Object.defineProperty(viewSpec, "canEdit", {
+    Object.defineProperty(viewSpec, 'canEdit', {
       get: () => {
         return Object.keys(viewSpec.elements).some(key => {
           const elm = viewSpec.elements[key];
           return (!isViewElementObjectOrList(elm) && elm.config);
         });
-      }
+      },
     });
 
     // merge in all uses references and resolve groupings
-    const usesRefs = this.extractNodes(statement, "uses");
+    const usesRefs = this.extractNodes(statement, 'uses');
     if (usesRefs && usesRefs.length > 0) {
 
       viewSpec.uses = (viewSpec.uses || []);
-      const resolveFunctions : ((parentElementPath: string)=>void)[] = [];
+      const resolveFunctions: ((parentElementPath: string) => void)[] = [];
 
       for (let i = 0; i < usesRefs.length; ++i) {
         const groupingName = usesRefs[i].arg;
@@ -951,7 +1008,7 @@ export class YangParser {
         }
 
         viewSpec.uses.push(this.resolveReferencePath(groupingName, context));
-        
+
         resolveFunctions.push((parentElementPath: string) => {
           const groupingViewSpec = this.resolveGrouping(groupingName, context);
           if (groupingViewSpec) {
@@ -963,10 +1020,22 @@ export class YangParser {
             Object.keys(groupingViewSpec.elements).forEach(key => {
               const elm = groupingViewSpec.elements[key];
               // a useRef on root level need a namespace
+              const resolvedWhen = elm.when && groupingViewSpec.when
+                ? {
+                  type: WhenTokenType.AND,
+                  left: elm.when,
+                  right: groupingViewSpec.when,
+                }
+                : elm.when || groupingViewSpec.when;  
+              
+              const resolvedIfFeature = elm.ifFeature
+                ? `(${groupingViewSpec.ifFeature}) and (${elm.ifFeature})`
+                : groupingViewSpec.ifFeature;
+
               viewSpec.elements[parentId === 0 ? `${module.name}:${key}` : key] = {
                 ...elm,
-                when: elm.when ? `(${groupingViewSpec.when}) and (${elm.when})` : groupingViewSpec.when,
-                ifFeature: elm.ifFeature ? `(${groupingViewSpec.ifFeature}) and (${elm.ifFeature})` : groupingViewSpec.ifFeature,
+                when: resolvedWhen,
+                ifFeature: resolvedIfFeature,
               };
             });
           }
@@ -974,19 +1043,19 @@ export class YangParser {
       }
 
       viewSpec.uses[ResolveFunction] = (parentElementPath: string) => {
-        const currentElementPath = `${parentElementPath} -> ${viewSpec.ns}:${viewSpec.name}`;  
+        const currentElementPath = `${parentElementPath} -> ${viewSpec.ns}:${viewSpec.name}`;
         resolveFunctions.forEach(resolve => {
-            try {
-                resolve(currentElementPath);
-            } catch (error) {
-                console.error(error);
-            }
+          try {
+            resolve(currentElementPath);
+          } catch (error) {
+            console.error(error);
+          }
         });
         // console.log("Resolved "+currentElementPath, viewSpec);
         if (viewSpec?.uses) {
           viewSpec.uses[ResolveFunction] = undefined;
         }
-      }
+      };
 
       this._groupingsToResolve.push(viewSpec);
     }
@@ -1020,28 +1089,28 @@ export class YangParser {
   /** Extracts the UI View from the type in the cur statement. */
   private getViewElement(cur: Statement, module: Module, parentId: number, currentPath: string, isList: boolean): ViewElement {
 
-    const type = this.extractValue(cur, "type");
-    const defaultVal = this.extractValue(cur, "default") || undefined;
-    const description = this.extractValue(cur, "description") || undefined;
+    const type = this.extractValue(cur, 'type');
+    const defaultVal = this.extractValue(cur, 'default') || undefined;
+    const description = this.extractValue(cur, 'description') || undefined;
 
-    const configValue = this.extractValue(cur, "config");
-    const config = configValue == null ? true : configValue.toLocaleLowerCase() !== "false";
+    const configValue = this.extractValue(cur, 'config');
+    const config = configValue == null ? true : configValue.toLocaleLowerCase() !== 'false';
 
-    const extractRange = (min: number, max: number, property: string = "range"): { expression: Expression<YangRange> | undefined, min: number, max: number } => {
-      const ranges = this.extractValue(this.extractNodes(cur, "type")[0]!, property) || undefined;
-      const range = ranges ?.replace(/min/i, String(min)).replace(/max/i, String(max)).split("|").map(r => {
+    const extractRange = (min: number, max: number, property: string = 'range'): { expression: Expression<YangRange> | undefined; min: number; max: number } => {
+      const ranges = this.extractValue(this.extractNodes(cur, 'type')[0]!, property) || undefined;
+      const range = ranges?.replace(/min/i, String(min)).replace(/max/i, String(max)).split('|').map(r => {
         let minValue: number;
         let maxValue: number;
-        
+
         if (r.indexOf('..') > -1) {
-            const [minStr, maxStr] = r.split('..');
-            minValue = Number(minStr);
-            maxValue = Number(maxStr);
-        } else if (!isNaN(maxValue = Number(r && r.trim() )) ) {
-            minValue = maxValue;
+          const [minStr, maxStr] = r.split('..');
+          minValue = Number(minStr);
+          maxValue = Number(maxStr);
+        } else if (!isNaN(maxValue = Number(r && r.trim()))) {
+          minValue = maxValue;
         } else {
-            minValue = min,
-            maxValue = max;
+          minValue = min,
+          maxValue = max;
         }
 
         if (minValue > min) min = minValue;
@@ -1049,7 +1118,7 @@ export class YangParser {
 
         return {
           min: minValue,
-          max: maxValue
+          max: maxValue,
         };
       });
       return {
@@ -1058,21 +1127,22 @@ export class YangParser {
         expression: range && range.length === 1
           ? range[0]
           : range && range.length > 1
-            ? { operation: "OR", arguments: range }
-            : undefined
-      }
+            ? { operation: 'OR', arguments: range }
+            : undefined,
+      };
     };
 
     const extractPattern = (): Expression<RegExp> | undefined => {
-      const pattern = this.extractNodes(this.extractNodes(cur, "type")[0]!, "pattern").map(p => p.arg!).filter(p => !!p).map(p => `^${p.replace(/(?:\\(.))/g, '$1')}$`);
+    // 2023.01.26 decision MF & SKO: we will no longer remove the backslashes from the pattern, seems to be a bug in the original code
+      const pattern = this.extractNodes(this.extractNodes(cur, 'type')[0]!, 'pattern').map(p => p.arg!).filter(p => !!p).map(p => `^${p/*.replace(/(?:\\(.))/g, '$1')*/}$`);
       return pattern && pattern.length == 1
         ? new RegExp(pattern[0])
         : pattern && pattern.length > 1
-          ? { operation: "AND", arguments: pattern.map(p => new RegExp(p)) }
+          ? { operation: 'AND', arguments: pattern.map(p => new RegExp(p)) }
           : undefined;
-    }
+    };
 
-    const mandatory = this.extractValue(cur, "mandatory") === "true" || false;
+    const mandatory = this.extractValue(cur, 'mandatory') === 'true' || false;
 
     if (!cur.arg) {
       throw new Error(`Module: [${module.name}]. Found element without name.`);
@@ -1084,159 +1154,159 @@ export class YangParser {
 
     const element: ViewElementBase = {
       id: parentId === 0 ? `${module.name}:${cur.arg}` : cur.arg,
-      label: cur.arg, 
+      label: cur.arg,
       path: currentPath,
-      module: module.name || "",
+      module: module.name || '',
       config: config,
       mandatory: mandatory,
       isList: isList,
       default: defaultVal,
-      description: description
+      description: description,
     };
 
-    if (type === "string") {
-      const length = extractRange(0, +18446744073709551615, "length");
+    if (type === 'string') {
+      const length = extractRange(0, +18446744073709551615, 'length');
       return ({
         ...element,
-        uiType: "string",
+        uiType: 'string',
         length: length.expression,
         pattern: extractPattern(),
       });
-    } else if (type === "boolean") {
+    } else if (type === 'boolean') {
       return ({
         ...element,
-        uiType: "boolean"
+        uiType: 'boolean',
       });
-    } else if (type === "uint8") {
+    } else if (type === 'uint8') {
       const range = extractRange(0, +255);
       return ({
         ...element,
-        uiType: "number",
+        uiType: 'number',
         range: range.expression,
         min: range.min,
         max: range.max,
-        units: this.extractValue(cur, "units") || undefined,
-        format: this.extractValue(cur, "format") || undefined,
+        units: this.extractValue(cur, 'units') || undefined,
+        format: this.extractValue(cur, 'format') || undefined,
       });
-    } else if (type === "uint16") {
+    } else if (type === 'uint16') {
       const range = extractRange(0, +65535);
       return ({
         ...element,
-        uiType: "number",
+        uiType: 'number',
         range: range.expression,
         min: range.min,
         max: range.max,
-        units: this.extractValue(cur, "units") || undefined,
-        format: this.extractValue(cur, "format") || undefined,
+        units: this.extractValue(cur, 'units') || undefined,
+        format: this.extractValue(cur, 'format') || undefined,
       });
-    } else if (type === "uint32") {
+    } else if (type === 'uint32') {
       const range = extractRange(0, +4294967295);
       return ({
         ...element,
-        uiType: "number",
+        uiType: 'number',
         range: range.expression,
         min: range.min,
         max: range.max,
-        units: this.extractValue(cur, "units") || undefined,
-        format: this.extractValue(cur, "format") || undefined,
+        units: this.extractValue(cur, 'units') || undefined,
+        format: this.extractValue(cur, 'format') || undefined,
       });
-    } else if (type === "uint64") {
+    } else if (type === 'uint64') {
       const range = extractRange(0, +18446744073709551615);
       return ({
         ...element,
-        uiType: "number",
+        uiType: 'number',
         range: range.expression,
         min: range.min,
         max: range.max,
-        units: this.extractValue(cur, "units") || undefined,
-        format: this.extractValue(cur, "format") || undefined,
+        units: this.extractValue(cur, 'units') || undefined,
+        format: this.extractValue(cur, 'format') || undefined,
       });
-    } else if (type === "int8") {
+    } else if (type === 'int8') {
       const range = extractRange(-128, +127);
       return ({
         ...element,
-        uiType: "number",
+        uiType: 'number',
         range: range.expression,
         min: range.min,
         max: range.max,
-        units: this.extractValue(cur, "units") || undefined,
-        format: this.extractValue(cur, "format") || undefined,
+        units: this.extractValue(cur, 'units') || undefined,
+        format: this.extractValue(cur, 'format') || undefined,
       });
-    } else if (type === "int16") {
+    } else if (type === 'int16') {
       const range = extractRange(-32768, +32767);
       return ({
         ...element,
-        uiType: "number",
+        uiType: 'number',
         range: range.expression,
         min: range.min,
         max: range.max,
-        units: this.extractValue(cur, "units") || undefined,
-        format: this.extractValue(cur, "format") || undefined,
+        units: this.extractValue(cur, 'units') || undefined,
+        format: this.extractValue(cur, 'format') || undefined,
       });
-    } else if (type === "int32") {
+    } else if (type === 'int32') {
       const range = extractRange(-2147483648, +2147483647);
       return ({
         ...element,
-        uiType: "number",
+        uiType: 'number',
         range: range.expression,
         min: range.min,
         max: range.max,
-        units: this.extractValue(cur, "units") || undefined,
-        format: this.extractValue(cur, "format") || undefined,
+        units: this.extractValue(cur, 'units') || undefined,
+        format: this.extractValue(cur, 'format') || undefined,
       });
-    } else if (type === "int64") {
+    } else if (type === 'int64') {
       const range = extractRange(-9223372036854775808, +9223372036854775807);
       return ({
         ...element,
-        uiType: "number",
+        uiType: 'number',
         range: range.expression,
         min: range.min,
         max: range.max,
-        units: this.extractValue(cur, "units") || undefined,
-        format: this.extractValue(cur, "format") || undefined,
+        units: this.extractValue(cur, 'units') || undefined,
+        format: this.extractValue(cur, 'format') || undefined,
       });
-    } else if (type === "decimal64") {
+    } else if (type === 'decimal64') {
       // decimalRange
-      const fDigits = Number(this.extractValue(this.extractNodes(cur, "type")[0]!, "fraction-digits")) || -1;
+      const fDigits = Number(this.extractValue(this.extractNodes(cur, 'type')[0]!, 'fraction-digits')) || -1;
       if (fDigits === -1) {
         throw new Error(`Module: [${module.name}][${currentPath}][${cur.arg}]. Found decimal64 with invalid fraction-digits.`);
       }
       const range = extractRange(YangParser.decimalRange[fDigits].min, YangParser.decimalRange[fDigits].max);
       return ({
         ...element,
-        uiType: "number",
+        uiType: 'number',
         fDigits: fDigits,
         range: range.expression,
         min: range.min,
         max: range.max,
-        units: this.extractValue(cur, "units") || undefined,
-        format: this.extractValue(cur, "format") || undefined,
+        units: this.extractValue(cur, 'units') || undefined,
+        format: this.extractValue(cur, 'format') || undefined,
       });
-    } else if (type === "enumeration") {
-      const typeNode = this.extractNodes(cur, "type")[0]!;
-      const enumNodes = this.extractNodes(typeNode, "enum");
+    } else if (type === 'enumeration') {
+      const typeNode = this.extractNodes(cur, 'type')[0]!;
+      const enumNodes = this.extractNodes(typeNode, 'enum');
       return ({
         ...element,
-        uiType: "selection",
+        uiType: 'selection',
         options: enumNodes.reduce<{ key: string; value: string; description?: string }[]>((acc, enumNode) => {
           if (!enumNode.arg) {
             throw new Error(`Module: [${module.name}][${currentPath}][${cur.arg}]. Found option without name.`);
           }
-          const ifClause = this.extractValue(enumNode, "if-feature");
-          const value = this.extractValue(enumNode, "value");
+          // const ifClause = this.extractValue(enumNode, 'if-feature');
+          const value = this.extractValue(enumNode, 'value');
           const enumOption = {
             key: enumNode.arg,
             value: value != null ? value : enumNode.arg,
-            description: this.extractValue(enumNode, "description") || undefined
+            description: this.extractValue(enumNode, 'description') || undefined,
           };
           // todo: ❗ handle the if clause ⚡
           acc.push(enumOption);
           return acc;
-        }, [])
+        }, []),
       });
-    } else if (type === "leafref") {
-      const typeNode = this.extractNodes(cur, "type")[0]!;
-      const vPath = this.extractValue(typeNode, "path");
+    } else if (type === 'leafref') {
+      const typeNode = this.extractNodes(cur, 'type')[0]!;
+      const vPath = this.extractValue(typeNode, 'path');
       if (!vPath) {
         throw new Error(`Module: [${module.name}][${currentPath}][${cur.arg}]. Found leafref without path.`);
       }
@@ -1244,11 +1314,11 @@ export class YangParser {
       const resolve = this.resolveReference.bind(this);
       const res: ViewElement = {
         ...element,
-        uiType: "reference",
+        uiType: 'reference',
         referencePath: refPath,
-        ref(this: ViewElement, currentPath: string) {
-          const elementPath = `${currentPath}/${cur.arg}`;  
-          
+        ref(this: ViewElement, basePath: string) {
+          const elementPath = `${basePath}/${cur.arg}`;
+
           const result = resolve(refPath, elementPath);
           if (!result) return undefined;
 
@@ -1262,20 +1332,20 @@ export class YangParser {
             isList: this.isList,
             default: this.default,
             description: this.description,
-          } as ViewElement , resolvedPath] || undefined;
-        }
+          } as ViewElement, resolvedPath] || undefined;
+        },
       };
       return res;
-    } else if (type === "identityref") {
-      const typeNode = this.extractNodes(cur, "type")[0]!;
-      const base = this.extractValue(typeNode, "base");
+    } else if (type === 'identityref') {
+      const typeNode = this.extractNodes(cur, 'type')[0]!;
+      const base = this.extractValue(typeNode, 'base');
       if (!base) {
         throw new Error(`Module: [${module.name}][${currentPath}][${cur.arg}]. Found identityref without base.`);
       }
       const res: ViewElement = {
         ...element,
-        uiType: "selection",
-        options: []
+        uiType: 'selection',
+        options: [],
       };
       this._identityToResolve.push(() => {
         const identity: Identity = this.resolveIdentity(base, module);
@@ -1288,29 +1358,29 @@ export class YangParser {
         res.options = identity.values.map(val => ({
           key: val.id,
           value: val.id,
-          description: val.description
+          description: val.description,
         }));
       });
       return res;
-    } else if (type === "empty") {
+    } else if (type === 'empty') {
       // todo: ❗ handle empty ⚡
       /*  9.11.  The empty Built-In Type
           The empty built-in type represents a leaf that does not have any
           value, it conveys information by its presence or absence. */
       return {
         ...element,
-        uiType: "empty",
+        uiType: 'empty',
       };
-    } else if (type === "union") {
+    } else if (type === 'union') {
       // todo: ❗ handle union ⚡
       /* 9.12.  The union Built-In Type */
-      const typeNode = this.extractNodes(cur, "type")[0]!;
-      const typeNodes = this.extractNodes(typeNode, "type");
+      const typeNode = this.extractNodes(cur, 'type')[0]!;
+      const typeNodes = this.extractNodes(typeNode, 'type');
 
       const resultingElement = {
         ...element,
-        uiType: "union",
-        elements: []
+        uiType: 'union',
+        elements: [],
       } as ViewElementUnion;
 
       const resolveUnion = () => {
@@ -1318,13 +1388,13 @@ export class YangParser {
           const stm: Statement = {
             ...cur,
             sub: [
-              ...(cur.sub ?.filter(s => s.key !== "type") || []),
-              node
-            ]
+              ...(cur.sub?.filter(s => s.key !== 'type') || []),
+              node,
+            ],
           };
           return {
             ...this.getViewElement(stm, module, parentId, currentPath, isList),
-            id: node.arg!
+            id: node.arg!,
           };
         }));
       };
@@ -1332,34 +1402,34 @@ export class YangParser {
       this._unionsToResolve.push(resolveUnion);
 
       return resultingElement;
-    } else if (type === "bits") {
-      const typeNode = this.extractNodes(cur, "type")[0]!;
-      const bitNodes = this.extractNodes(typeNode, "bit");
+    } else if (type === 'bits') {
+      const typeNode = this.extractNodes(cur, 'type')[0]!;
+      const bitNodes = this.extractNodes(typeNode, 'bit');
       return {
         ...element,
-        uiType: "bits",
-        flags: bitNodes.reduce<{ [name: string]: number | undefined; }>((acc, bitNode) => {
+        uiType: 'bits',
+        flags: bitNodes.reduce<{ [name: string]: number | undefined }>((acc, bitNode) => {
           if (!bitNode.arg) {
             throw new Error(`Module: [${module.name}][${currentPath}][${cur.arg}]. Found bit without name.`);
           }
-          const ifClause = this.extractValue(bitNode, "if-feature");
-          const pos = Number(this.extractValue(bitNode, "position"));
+          // const ifClause = this.extractValue(bitNode, 'if-feature');
+          const pos = Number(this.extractValue(bitNode, 'position'));
           acc[bitNode.arg] = pos === pos ? pos : undefined;
           return acc;
-        }, {})
+        }, {}),
       };
-    } else if (type === "binary") {
+    } else if (type === 'binary') {
       return {
         ...element,
-        uiType: "binary",
-        length: extractRange(0, +18446744073709551615, "length"),
+        uiType: 'binary',
+        length: extractRange(0, +18446744073709551615, 'length'),
       };
-    } else if (type === "instance-identifier") {
+    } else if (type === 'instance-identifier') {
       // https://tools.ietf.org/html/rfc7950#page-168
       return {
         ...element,
-        uiType: "string",
-        length: extractRange(0, +18446744073709551615, "length"),
+        uiType: 'string',
+        length: extractRange(0, +18446744073709551615, 'length'),
       };
     } else {
       // not a build in type, need to resolve type
@@ -1374,13 +1444,13 @@ export class YangParser {
       }
 
       // spoof date type here from special string type
-      if ((type === 'date-and-time' || type.endsWith(':date-and-time') ) && typeRef.module === "ietf-yang-types") {
-          return {
-             ...typeRef,
-             ...element,
-             description: description,
-             uiType: "date", 
-          };
+      if ((type === 'date-and-time' || type.endsWith(':date-and-time')) && typeRef.module === 'ietf-yang-types') {
+        return {
+          ...typeRef,
+          ...element,
+          description: description,
+          uiType: 'date',
+        };
       }
 
       return ({
@@ -1391,27 +1461,27 @@ export class YangParser {
     }
   }
 
-  private resolveStringType(parentElement: ViewElementString, pattern: Expression<RegExp> | undefined, length: { expression: Expression<YangRange> | undefined, min: number, max: number }) {
+  private resolveStringType(parentElement: ViewElementString, pattern: Expression<RegExp> | undefined, length: { expression: Expression<YangRange> | undefined; min: number; max: number }) {
     return {
       ...parentElement,
       pattern: pattern != null && parentElement.pattern
-        ? { operation: "AND", arguments: [pattern, parentElement.pattern] }
+        ? { operation: 'AND', arguments: [pattern, parentElement.pattern] }
         : parentElement.pattern
           ? parentElement.pattern
           : pattern,
       length: length.expression != null && parentElement.length
-        ? { operation: "AND", arguments: [length.expression, parentElement.length] }
+        ? { operation: 'AND', arguments: [length.expression, parentElement.length] }
         : parentElement.length
           ? parentElement.length
-          : length ?.expression,
+          : length?.expression,
     } as ViewElementString;
   }
 
-  private resolveNumberType(parentElement: ViewElementNumber, range: { expression: Expression<YangRange> | undefined, min: number, max: number }) {
+  private resolveNumberType(parentElement: ViewElementNumber, range: { expression: Expression<YangRange> | undefined; min: number; max: number }) {
     return {
       ...parentElement,
       range: range.expression != null && parentElement.range
-        ? { operation: "AND", arguments: [range.expression, parentElement.range] }
+        ? { operation: 'AND', arguments: [range.expression, parentElement.range] }
         : parentElement.range
           ? parentElement.range
           : range,
@@ -1421,7 +1491,7 @@ export class YangParser {
   }
 
   private resolveReferencePath(vPath: string, module: Module) {
-    const vPathParser = /(?:(?:([^\/\:]+):)?([^\/]+))/g // 1 = opt: namespace / 2 = property
+    const vPathParser = /(?:(?:([^\/\:]+):)?([^\/]+))/g; // 1 = opt: namespace / 2 = property
     return vPath.replace(vPathParser, (_, ns, property) => {
       const nameSpace = ns && module.imports[ns] || module.name;
       return `${nameSpace}:${property}`;
@@ -1429,20 +1499,20 @@ export class YangParser {
   }
 
   private resolveReference(vPath: string, currentPath: string) {
-    const vPathParser = /(?:(?:([^\/\[\]\:]+):)?([^\/\[\]]+)(\[[^\]]+\])?)/g // 1 = opt: namespace / 2 = property / 3 = opt: indexPath
+    const vPathParser = /(?:(?:([^\/\[\]\:]+):)?([^\/\[\]]+)(\[[^\]]+\])?)/g; // 1 = opt: namespace / 2 = property / 3 = opt: indexPath
     let element: ViewElement | null = null;
-    let moduleName = "";
+    let moduleName = '';
 
     const vPathParts = splitVPath(vPath, vPathParser).map(p => ({ ns: p[1], property: p[2], ind: p[3] }));
-    const resultPathParts = !vPath.startsWith("/")
-      ? splitVPath(currentPath, vPathParser).map(p => { moduleName = p[1] || moduleName ; return { ns: moduleName, property: p[2], ind: p[3] } })
+    const resultPathParts = !vPath.startsWith('/')
+      ? splitVPath(currentPath, vPathParser).map(p => { moduleName = p[1] || moduleName; return { ns: moduleName, property: p[2], ind: p[3] }; })
       : [];
 
     for (let i = 0; i < vPathParts.length; ++i) {
       const vPathPart = vPathParts[i];
-      if (vPathPart.property === "..") {
+      if (vPathPart.property === '..') {
         resultPathParts.pop();
-      } else if (vPathPart.property !== ".") {
+      } else if (vPathPart.property !== '.') {
         resultPathParts.push(vPathPart);
       }
     }
@@ -1453,30 +1523,30 @@ export class YangParser {
       if (j === 0) {
         moduleName = pathPart.ns;
         const rootModule = this._modules[moduleName];
-        if (!rootModule) throw new Error("Could not resolve module [" + moduleName + "].\r\n" + vPath);
+        if (!rootModule) throw new Error('Could not resolve module [' + moduleName + '].\r\n' + vPath);
         element = rootModule.elements[`${pathPart.ns}:${pathPart.property}`];
       } else if (element && isViewElementObjectOrList(element)) {
         const view: ViewSpecification = this._views[+element.viewId];
         if (moduleName !== pathPart.ns) {
           moduleName = pathPart.ns;
-        }   
+        }
         element = view.elements[pathPart.property] || view.elements[`${moduleName}:${pathPart.property}`];
       } else {
-        throw new Error("Could not resolve reference.\r\n" + vPath);
+        throw new Error('Could not resolve reference.\r\n' + vPath);
       }
-      if (!element) throw new Error("Could not resolve path [" + pathPart.property + "] in [" + currentPath + "] \r\n" + vPath);
+      if (!element) throw new Error('Could not resolve path [' + pathPart.property + '] in [' + currentPath + '] \r\n' + vPath);
     }
 
-    moduleName = ""; // create the vPath for the resolved element, do not add the element itself this will be done later in the res(...) function
-    return [element, resultPathParts.slice(0,-1).map(p => `${moduleName !== p.ns ? `${moduleName=p.ns}:` : ""}${p.property}${p.ind || ''}`).join("/")];
+    moduleName = ''; // create the vPath for the resolved element, do not add the element itself this will be done later in the res(...) function
+    return [element, resultPathParts.slice(0, -1).map(p => `${moduleName !== p.ns ? `${moduleName = p.ns}:` : ''}${p.property}${p.ind || ''}`).join('/')];
   }
 
   private resolveView(vPath: string) {
-    const vPathParser = /(?:(?:([^\/\[\]\:]+):)?([^\/\[\]]+)(\[[^\]]+\])?)/g // 1 = opt: namespace / 2 = property / 3 = opt: indexPath
+    const vPathParser = /(?:(?:([^\/\[\]\:]+):)?([^\/\[\]]+)(\[[^\]]+\])?)/g; // 1 = opt: namespace / 2 = property / 3 = opt: indexPath
     let element: ViewElement | null = null;
     let partMatch: RegExpExecArray | null;
     let view: ViewSpecification | null = null;
-    let moduleName = "";
+    let moduleName = '';
     if (vPath) do {
       partMatch = vPathParser.exec(vPath);
       if (partMatch) {
@@ -1498,13 +1568,13 @@ export class YangParser {
         }
         if (!element) return null;
       }
-    } while (partMatch)
+    } while (partMatch);
     return element && isViewElementObjectOrList(element) && this._views[+element.viewId] || null;
   }
 
   private resolveType(type: string, module: Module) {
-    const collonInd = type.indexOf(":");
-    const preFix = collonInd > -1 ? type.slice(0, collonInd) : "";
+    const collonInd = type.indexOf(':');
+    const preFix = collonInd > -1 ? type.slice(0, collonInd) : '';
     const typeName = collonInd > -1 ? type.slice(collonInd + 1) : type;
 
     const res = preFix
@@ -1514,8 +1584,8 @@ export class YangParser {
   }
 
   private resolveGrouping(grouping: string, module: Module) {
-    const collonInd = grouping.indexOf(":");
-    const preFix = collonInd > -1 ? grouping.slice(0, collonInd) : "";
+    const collonInd = grouping.indexOf(':');
+    const preFix = collonInd > -1 ? grouping.slice(0, collonInd) : '';
     const groupingName = collonInd > -1 ? grouping.slice(collonInd + 1) : grouping;
 
     return preFix
@@ -1525,8 +1595,8 @@ export class YangParser {
   }
 
   private resolveIdentity(identity: string, module: Module) {
-    const collonInd = identity.indexOf(":");
-    const preFix = collonInd > -1 ? identity.slice(0, collonInd) : "";
+    const collonInd = identity.indexOf(':');
+    const preFix = collonInd > -1 ? identity.slice(0, collonInd) : '';
     const identityName = collonInd > -1 ? identity.slice(collonInd + 1) : identity;
 
     return preFix
index 57caf07..0d37c7d 100644 (file)
@@ -10,6 +10,7 @@ const path = require("path");
 const webpack = require("webpack");
 const CopyWebpackPlugin = require("copy-webpack-plugin");
 const TerserPlugin = require('terser-webpack-plugin');
+const proxyConf = require('../../proxy.conf');
 
 const policies = require('./policies.json');
 
@@ -59,6 +60,16 @@ module.exports = (env) => {
         use: [{
           loader: "babel-loader"
         }]
+      },{
+        //don't minify images
+        test: /\.(png|gif|jpg|svg)$/,
+        use: [{
+          loader: 'url-loader',
+          options: {
+            limit: 10,
+            name: './images/[name].[ext]'
+          }
+        }]
       }]
     },
 
@@ -133,56 +144,7 @@ module.exports = (env) => {
       before: function(app, server, compiler) {
         app.get('/oauth/policies',(_, res) => res.json(policies));
       },
-       proxy: {
-        "/about": {
-          target: "http://sdnr:8181",
-          secure: false
-        }, 
-        "/yang-schema/": {
-          target: "http://sdnr:8181",
-          secure: false
-        },   
-        "/oauth/": {
-          target: "http://sdnr:8181",
-          secure: false
-        },
-        "/database/": {
-          target: "http://sdnr:8181",
-          secure: false
-        },
-        "/restconf/": {
-          target: "http://sdnr:8181",
-          secure: false
-        },
-        "/rests/": {
-          target: "http://sdnr:8181",
-          secure: false
-        },
-        "/help/": {
-          target: "http://sdnr:8181",
-          secure: false
-        },
-         "/about/": {
-          target: "http://sdnr:8181",
-          secure: false
-        },
-        "/tree/": {
-          target: "http://sdnr:8181",
-          secure: false
-        },
-        "/websocket": {
-          target: "http://sdnr:8181",
-          ws: true,
-          changeOrigin: true,
-          secure: false
-        },
-        "/apidoc": {
-          target: "http://sdnr:8181",
-          ws: true,
-          changeOrigin: true,
-          secure: false
-        }
-      }
+      proxy: proxyConf,
     }
   }];
 }
index a31824a..9a3c35b 100644 (file)
@@ -29,6 +29,7 @@
     "@odlux/framework": "*"
   },
   "peerDependencies": {
+    "@fortawesome/free-solid-svg-icons": "5.6.3",
     "@types/classnames": "2.2.6",
     "@types/flux": "3.1.8",
     "@types/jquery": "3.3.10",
index c12048e..c9509c7 100644 (file)
@@ -19,6 +19,7 @@
   ~ ============LICENSE_END=======================================================
   ~
   -->
+
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
                         <!-- optional: default phase is "generate-resources" -->
                         <phase>initialize</phase>
                         <configuration>
-                            <nodeVersion>v12.13.0</nodeVersion>
+                            <nodeVersion>v12.22.0</nodeVersion>
                             <yarnVersion>v1.22.10</yarnVersion>
                         </configuration>
                     </execution>
index 26aa8d2..948f2aa 100644 (file)
@@ -1,5 +1,3 @@
-// update action erstellen, die unterscheiden kann, ob die eine oder die andere Ansicht gerade aktive ist und diese katualisiert.
-// Diese action wird dann bei jeder aktualisierung in den anderen Actions und bei eintreffen von notifikationen verwendet.
 
 /**
  * ============LICENSE_START========================================================================
  * ============LICENSE_END==========================================================================
  */
 
+/**
+ * Create an update action that can distinguish whether one or the other view is currently active and update it.
+ * This action is then used for each update in the other actions and when notifications arrive.
+ * create an update action that can distinguish whether one or the other view is currently active and update it.
+ * This action is then used for each update in the other actions and when notifications arrive.
+ */
+
 import { Action } from '../../../../framework/src/flux/action';
 import { Dispatch } from '../../../../framework/src/flux/store';
 import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
 
-import { networkElementsReloadAction } from '../handlers/networkElementsHandler';
 import { connectionStatusLogReloadAction } from '../handlers/connectionStatusLogHandler';
-
-import { PanelId } from '../models/panelId';
+import { networkElementsReloadAction } from '../handlers/networkElementsHandler';
 import { guiCutThrough } from '../models/guiCutTrough';
-import { connectService} from '../services/connectService';
+import { PanelId } from '../models/panelId';
+import { connectService } from '../services/connectService';
 
 
 export class SetPanelAction extends Action {
@@ -51,7 +55,7 @@ export class RemoveWebUri extends Action {
 
 export const removeWebUriAction = (nodeId: string) => {
   return new RemoveWebUri(nodeId);
-}
+};
 
 export class SetWeburiSearchBusy extends Action {
   constructor(public isbusy: boolean) {
@@ -68,7 +72,7 @@ export const findWebUrisForGuiCutThroughAsyncAction = (networkElementIds: string
     return;
   isBusy = true;
 
-  const { connect: { guiCutThrough, networkElements } } = getState();
+  const { connect: { guiCutThrough: guiCutThrough2, networkElements } } = getState();
 
   let notConnectedElements: string[] = [];
   let elementsToSearch: string[] = [];
@@ -78,16 +82,16 @@ export const findWebUrisForGuiCutThroughAsyncAction = (networkElementIds: string
   networkElementIds.forEach(id => {
     const item = networkElements.rows.find((ne) => ne.id === id);
     if (item) {
-      if (item.status === "Connected") {
+      if (item.status === 'Connected') {
 
         // if (item.coreModelCapability !== "Unsupported") {
         // element is connected and is added to search list, if it doesn't exist already
-        const exists = guiCutThrough.searchedElements.filter(element => element.id === id).length > 0;
+        const exists = guiCutThrough2.searchedElements.filter(element => element.id === id).length > 0;
         if (!exists) {
           elementsToSearch.push(id);
 
           //element was found previously, but wasn't connected
-          if (guiCutThrough.notSearchedElements.length > 0 && guiCutThrough.notSearchedElements.includes(id)) {
+          if (guiCutThrough2.notSearchedElements.length > 0 && guiCutThrough2.notSearchedElements.includes(id)) {
             prevFoundElements.push(id);
           }
         }
@@ -104,10 +108,9 @@ export const findWebUrisForGuiCutThroughAsyncAction = (networkElementIds: string
         //     }
         //   }
         // }
-      }
-      else {
+      } else {
         // element isn't connected and cannot be searched for a weburi
-        if (!guiCutThrough.notSearchedElements.includes(id)) {
+        if (!guiCutThrough2.notSearchedElements.includes(id)) {
           notConnectedElements.push(item.id as string);
         }
       }
@@ -121,18 +124,17 @@ export const findWebUrisForGuiCutThroughAsyncAction = (networkElementIds: string
   }
   isBusy = false;
 
-}
+};
 
 export const setPanelAction = (panelId: PanelId) => {
   return new SetPanelAction(panelId);
-}
+};
 
 export const updateCurrentViewAsyncAction = () => (dispatch: Dispatch, getState: () => IApplicationStoreState) => {
   const { connect: { currentOpenPanel } } = getState();
-  if (currentOpenPanel === "NetworkElements") {
+  if (currentOpenPanel === 'NetworkElements') {
     return dispatch(networkElementsReloadAction);
-  }
-  else {
+  } else {
     return dispatch(connectionStatusLogReloadAction);
   }
 };
index bb744e2..120f991 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
- import { Action } from '../../../../framework/src/flux/action';
- import { Dispatch } from '../../../../framework/src/flux/store';
+import { Action } from '../../../../framework/src/flux/action';
+import { Dispatch } from '../../../../framework/src/flux/store';
  
- import { Module, TopologyNode } from '../models/topologyNetconf';
- import { connectService } from '../services/connectService';
+import { Module, TopologyNode } from '../models/topologyNetconf';
+import { connectService } from '../services/connectService';
  
- /** 
+/** 
   * Represents the base action. 
   */
- export class BaseAction extends Action { }
+export class BaseAction extends Action { }
  
- /** 
+/** 
   * Represents an action causing the store to load all element Yang capabilities.
   */
- export class LoadAllElementInfoAction extends BaseAction { }
+export class LoadAllElementInfoAction extends BaseAction { }
  
- /** 
+/** 
   * Represents an action causing the store to update element Yang capabilities. 
   */
- export class AllElementInfoLoadedAction extends BaseAction {
-   /**
+export class AllElementInfoLoadedAction extends BaseAction {
+  /**
     * Initialize this instance.
     * @param elementInfo The information of the element which is returned.
     */
-   constructor(public elementInfo: TopologyNode | null, public error?: string) {
-     super();
-   }
- }
+  constructor(public elementInfo: TopologyNode | null, public error?: string) {
+    super();
+  }
+}
  
- /** 
+/** 
   * Represents an action causing the store to update element Yang capabilities Module Features. 
   */
- export class AllElementInfoFeatureLoadedAction extends BaseAction {
-   /**
+export class AllElementInfoFeatureLoadedAction extends BaseAction {
+  /**
     * Initialize this instance.
     * @param elementFeatureInfo The information of the element which is returned.
     */
-   constructor(public elementFeatureInfo: Module[] | null | undefined, public error?: string) {
-     super();
-   }
- }
+  constructor(public elementFeatureInfo: Module[] | null | undefined, public error?: string) {
+    super();
+  }
+}
  
- /** 
+/** 
   * Represents an asynchronous thunk  action to load all yang capabilities. 
   */
- export const loadAllInfoElementAsync = (nodeId: string) => (dispatch: Dispatch) => {
-   dispatch(new LoadAllElementInfoAction());
-   connectService.infoNetworkElement(nodeId).then(info => {
-     dispatch(new AllElementInfoLoadedAction(info));
-   }, error => {
-     dispatch(new AllElementInfoLoadedAction(null, error));
-   });
- } 
+export const loadAllInfoElementAsync = (nodeId: string) => (dispatch: Dispatch) => {
+  dispatch(new LoadAllElementInfoAction());
+  connectService.infoNetworkElement(nodeId).then(info => {
+    dispatch(new AllElementInfoLoadedAction(info));
+  }, error => {
+    dispatch(new AllElementInfoLoadedAction(null, error));
+  });
+}; 
  
- /** 
+/** 
   * Represents an asynchronous thunk  action to load all yang features. 
   */
- export const loadAllInfoElementFeaturesAsync = (nodeId: string) => (dispatch: Dispatch) => {
-   dispatch(new LoadAllElementInfoAction());
-   connectService.infoNetworkElementFeatures(nodeId).then(infoFeatures => {
-     dispatch(new AllElementInfoFeatureLoadedAction(infoFeatures));
-   }, error => {
-     dispatch(new AllElementInfoFeatureLoadedAction(null, error));
-   });
- } 
\ No newline at end of file
+export const loadAllInfoElementFeaturesAsync = (nodeId: string) => (dispatch: Dispatch) => {
+  dispatch(new LoadAllElementInfoAction());
+  connectService.infoNetworkElementFeatures(nodeId).then(infoFeatures => {
+    dispatch(new AllElementInfoFeatureLoadedAction(infoFeatures));
+  }, error => {
+    dispatch(new AllElementInfoFeatureLoadedAction(null, error));
+  });
+}; 
\ No newline at end of file
index 26ee767..11bac10 100644 (file)
@@ -32,7 +32,7 @@ export const mountNetworkElementAsyncActionCreator = (networkElement: NetworkEle
   return connectService.mountNetworkElement(networkElement).then((success) => {
     if (success) {
       dispatch(updateCurrentViewAsyncAction());
-      dispatch(new AddSnackbarNotification({ message: `Requesting mount [${networkElement.nodeId}]`, options: { variant: 'info' } }))
+      dispatch(new AddSnackbarNotification({ message: `Requesting mount [${networkElement.nodeId}]`, options: { variant: 'info' } }));
     } else {
       dispatch(new AddSnackbarNotification({ message: `Failed to mount [${networkElement.nodeId}]`, options: { variant: 'warning' } }));
     }
index 57f036e..d22a6c6 100644 (file)
@@ -30,7 +30,7 @@ export class BaseAction extends Action { }
 
 /** Represents an async thunk action creator to add an element to the network elements/nodes. */
 export const addNewNetworkElementAsyncActionCreator = (element: NetworkElementConnection) => async (dispatch: Dispatch) => {
-  const res = await connectService.createNetworkElement({ ...element });
+  await connectService.createNetworkElement({ ...element });
   dispatch(updateCurrentViewAsyncAction());
   dispatch(new AddSnackbarNotification({ message: `Successfully added [${element.nodeId}]`, options: { variant: 'success' } }));
 };
@@ -39,11 +39,10 @@ export const addNewNetworkElementAsyncActionCreator = (element: NetworkElementCo
 export const editNetworkElementAsyncActionCreator = (element: UpdateNetworkElement) => async (dispatch: Dispatch) => {
   const connectionStatus: ConnectionStatus[] = (await connectService.getNetworkElementConnectionStatus(element.id).then(ne => (ne))) || [];
   const currentConnectionStatus = connectionStatus[0].status;
-  if (currentConnectionStatus === "Disconnected") {
-    const res = await connectService.deleteNetworkElement(element);
-  }
-  else {
-    const res = await connectService.updateNetworkElement(element);
+  if (currentConnectionStatus === 'Disconnected') {
+    await connectService.deleteNetworkElement(element);
+  } else {
+    await connectService.updateNetworkElement(element);
   }
   dispatch(updateCurrentViewAsyncAction());
   dispatch(new AddSnackbarNotification({ message: `Successfully modified [${element.id}]`, options: { variant: 'success' } }));
@@ -52,7 +51,7 @@ export const editNetworkElementAsyncActionCreator = (element: UpdateNetworkEleme
 
 /** Represents an async thunk action creator to delete an element from network elements/nodes. */
 export const removeNetworkElementAsyncActionCreator = (element: UpdateNetworkElement) => async (dispatch: Dispatch) => {
-  const res = await connectService.deleteNetworkElement(element);
+  await connectService.deleteNetworkElement(element);
   await dispatch(unmountNetworkElementAsyncActionCreator(element && element.id));
   await dispatch(updateCurrentViewAsyncAction());
 };
index 1da16d9..65d23c4 100644 (file)
@@ -17,7 +17,6 @@
  */
 import { Action } from '../../../../framework/src/flux/action';
 import { Dispatch } from '../../../../framework/src/flux/store';
-import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
 
 import { TlsKeys } from '../models/networkElementConnection';
 import { connectService } from '../services/connectService';
@@ -36,14 +35,14 @@ export class LoadAllTlsKeyListAction extends BaseAction { }
  * Represents an action causing the store to get all TLS Keys.
  */
 export class AllTlsKeyListLoadedAction extends BaseAction {
-    /**
+  /**
      * Initialize this instance.
      * 
      * @param gets all the tlsKey list from the  database.
      */
-    constructor(public tlsList: TlsKeys[] | null, public error?: string) {
-        super();
-    }
+  constructor(public tlsList: TlsKeys[] | null, public error?: string) {
+    super();
+  }
 }
 
 /**
@@ -51,10 +50,10 @@ export class AllTlsKeyListLoadedAction extends BaseAction {
  */
 
 export const loadAllTlsKeyListAsync = () => async (dispatch: Dispatch) => {
-    dispatch(new LoadAllTlsKeyListAction());
-    connectService.getTlsKeys().then(TlsKeyList => {
-        dispatch(new AllTlsKeyListLoadedAction(TlsKeyList));
-    }).catch(error => {
-        dispatch(new AllTlsKeyListLoadedAction(null, error));
-    });
+  dispatch(new LoadAllTlsKeyListAction());
+  connectService.getTlsKeys().then(TlsKeyList => {
+    dispatch(new AllTlsKeyListLoadedAction(TlsKeyList));
+  }).catch(error => {
+    dispatch(new AllTlsKeyListLoadedAction(null, error));
+  });
 };
diff --git a/sdnr/wt/odlux/apps/connectApp/src/assets/icons/connectAppIcon.svg b/sdnr/wt/odlux/apps/connectApp/src/assets/icons/connectAppIcon.svg
new file mode 100644 (file)
index 0000000..5aca4fa
--- /dev/null
@@ -0,0 +1,22 @@
+<!-- highstreet technologies GmbH colour scheme 
+       Grey    #565656
+       LBlue #36A9E1
+       DBlue #246DA2
+       Green #003F2C / #006C4B
+       Yellw #C8D400
+       Red     #D81036
+-->
+
+<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 650 650">
+
+<g transform="translate(20,0) scale(1,1)">
+
+<path fill="#C8D400" d="M 121.5 10 c -11.5 4.1 -19.6 12.1 -23.6 23.5 c -1.8 5 -1.9 9.2 -1.9 73.9 l 0 48.5 l 37.5 0 l 37.5 0 l 0 -45.9 c 0 -40.3 -0.4 -67.9 -1 -71.2 c -2.3 -11.9 -10.6 -22.5 -21.6 -27.6 c -7.9 -3.6 -18.8 -4.1 -26.9 -1.2 z"/>
+
+<path fill="#C8D400" d="M 347.9 10 c -11.5 4.1 -19.6 12.1 -23.6 23.5 c -1.8 5 -1.9 9.2 -1.9 73.9 l 0 48.5 l 37.5 0 l 37.5 0 l 0 -45.9 c 0 -40.3 -0.4 -67.9 -1 -71.2 c -2.3 -11.9 -10.6 -22.5 -21.6 -27.6 c -7.9 -3.6 -18.8 -4.1 -26.9 -1.2 z"/>
+
+<path fill="#565656" d="m 32.5 190 c -4.9 2.2 -9.1 6.9 -10.5 11.9 c -0.7 2.4 -1 11.9 -0.8 26.3 l 0.3 22.6 l 2.9 4.1 c 4.5 6.5 9.1 8.2 22.4 8.2 l 11.2 0 l 0 17.7 c 0 35.3 3.1 59 10.5 80.3 c 21.5 61.6 70.5 105.9 135.3 122 l 4.2 1.1 l 0 57.9 c 1 27.9 4 72.9 75 75 c 177 -5.1 344 -100.1 345 -204.1 l 1 -75 c -49 124 -165 214 -337 217.1 c -5 -0.1 -7 -3.1 -7 -7.1 l 0 -63.8 l 4.2 -1.1 c 17.1 -4.4 26.1 -7.6 39.6 -14 c 51.7 -24.6 90.3 -74.2 101.7 -130.5 c 3 -14.6 4.5 -33.9 4.5 -57.7 l 0 -17.6 l 12.3 -0.4 c 11.1 -0.3 12.7 -0.5 15.9 -2.7 c 1.9 -1.3 4.6 -4 5.9 -5.9 c 2.3 -3.5 2.4 -4.2 2.7 -26.1 c 0.2 -14.4 -0.1 -23.9 -0.8 -26.3 c -1.4 -5 -5.6 -9.7 -10.5 -11.9 c -3.8 -1.8 -12.4 -1.9 -213 -1.9 c -200.6 0 -209.2 0.1 -213 1.9 z"/>
+
+
+</g>
+</svg>
index b240b24..6a8c924 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
-import connect, { IDispatcher, Connect } from '../../../../framework/src/flux/connect';
+import React from 'react';
+
+import Refresh from '@mui/icons-material/Refresh';
+
+import { ColumnType, MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table';
+import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect';
 import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
-import { MaterialTable, ColumnType, MaterialTableCtorType } from '../../../../framework/src/components/material-table';
-import  Refresh  from '@mui/icons-material/Refresh';
 
 import { createConnectionStatusLogActions, createConnectionStatusLogProperties } from '../handlers/connectionStatusLogHandler';
 import { NetworkElementConnectionLog } from '../models/networkElementConnectionLog';
@@ -37,36 +39,36 @@ const ConnectionStatusTable = MaterialTable as MaterialTableCtorType<NetworkElem
 
 type ConnectionStatusLogComponentProps = Connect<typeof mapProps, typeof mapDispatch>;
 type ConnectionStatusLogComponentState = {
-  refreshConnectionStatusLogEditorMode: RefreshConnectionStatusLogDialogMode
-}
+  refreshConnectionStatusLogEditorMode: RefreshConnectionStatusLogDialogMode;
+};
 
 let initialSorted = false;
 
 
-class ConnectionStatusLogComponent extends React.Component<ConnectionStatusLogComponentProps,ConnectionStatusLogComponentState > {
+class ConnectionStatusLogComponent extends React.Component<ConnectionStatusLogComponentProps, ConnectionStatusLogComponentState > {
   constructor(props: ConnectionStatusLogComponentProps) {
     super(props);
 
     this.state = {
-      refreshConnectionStatusLogEditorMode: RefreshConnectionStatusLogDialogMode.None
+      refreshConnectionStatusLogEditorMode: RefreshConnectionStatusLogDialogMode.None,
     };
   }
 
   render(): JSX.Element {
     const refreshConnectionStatusLogAction = {
-      icon: Refresh, tooltip: 'Refresh Connection Status Log Table',ariaLabel:'refresh', onClick: () => {
+      icon: Refresh, tooltip: 'Refresh Connection Status Log Table', ariaLabel:'refresh', onClick: () => {
         this.setState({
-          refreshConnectionStatusLogEditorMode: RefreshConnectionStatusLogDialogMode.RefreshConnectionStatusLogTable
+          refreshConnectionStatusLogEditorMode: RefreshConnectionStatusLogDialogMode.RefreshConnectionStatusLogTable,
         });
-      }
+      },
     };
 
     return (
     <>
       <ConnectionStatusTable stickyHeader tableId="connection-status-table" customActionButtons={[refreshConnectionStatusLogAction]}  columns={[
-        { property: "timestamp", title: "Timestamp", type: ColumnType.text },
-        { property: "nodeId", title: "Node ID", type: ColumnType.text },
-        { property: "status", title: "Connection Status", type: ColumnType.text },
+        { property: 'timestamp', title: 'Timestamp', type: ColumnType.text },
+        { property: 'nodeId', title: 'Node ID', type: ColumnType.text },
+        { property: 'status', title: 'Connection Status', type: ColumnType.text },
       ]} idProperty="id" {...this.props.connectionStatusLogActions} {...this.props.connectionStatusLogProperties} >
       </ConnectionStatusTable>
        <RefreshConnectionStatusLogDialog
@@ -75,17 +77,18 @@ class ConnectionStatusLogComponent extends React.Component<ConnectionStatusLogCo
       />
     </>
     );
-  };
+  }
 
   private onCloseRefreshConnectionStatusLogDialog = () => {
     this.setState({
-      refreshConnectionStatusLogEditorMode: RefreshConnectionStatusLogDialogMode.None
+      refreshConnectionStatusLogEditorMode: RefreshConnectionStatusLogDialogMode.None,
     });
-  }
+  };
+
   componentDidMount() {
     if (!initialSorted) {
       initialSorted = true;
-      this.props.connectionStatusLogActions.onHandleExplicitRequestSort("timestamp", "desc");
+      this.props.connectionStatusLogActions.onHandleExplicitRequestSort('timestamp', 'desc');
     } else {
       this.props.connectionStatusLogActions.onRefresh();
     }
index 5740ebd..b0db634 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
+import React from 'react';
 
 import Button from '@mui/material/Button';
-import TextField from '@mui/material/TextField';
 import Dialog from '@mui/material/Dialog';
 import DialogActions from '@mui/material/DialogActions';
 import DialogContent from '@mui/material/DialogContent';
 import DialogContentText from '@mui/material/DialogContentText';
 import DialogTitle from '@mui/material/DialogTitle';
-import { FormControl, InputLabel, Select, MenuItem, Typography, Radio, RadioGroup, Options, FormLabel, FormControlLabel } from '@mui/material';
-import { loadAllTlsKeyListAsync } from '../actions/tlsKeyActions';
-import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
+import FormControl from '@mui/material/FormControl';
+import FormControlLabel from '@mui/material/FormControlLabel';
+import InputLabel from '@mui/material/InputLabel';
+import MenuItem from '@mui/material/MenuItem';
+import Radio from '@mui/material/Radio';
+import RadioGroup from '@mui/material/RadioGroup';
+import Select from '@mui/material/Select';
+import TextField from '@mui/material/TextField';
+import Typography from '@mui/material/Typography';
 
-import { IDispatcher, connect, Connect } from '../../../../framework/src/flux/connect';
+import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect';
 
+import { removeWebUriAction } from '../actions/commonNetworkElementsActions';
+import { mountNetworkElementAsyncActionCreator, unmountNetworkElementAsyncActionCreator } from '../actions/mountedNetworkElementsActions';
 import {
-  editNetworkElementAsyncActionCreator,
-  addNewNetworkElementAsyncActionCreator,
-  removeNetworkElementAsyncActionCreator
+  addNewNetworkElementAsyncActionCreator, editNetworkElementAsyncActionCreator, removeNetworkElementAsyncActionCreator,
 } from '../actions/networkElementsActions';
-
-import { unmountNetworkElementAsyncActionCreator, mountNetworkElementAsyncActionCreator } from '../actions/mountedNetworkElementsActions';
-import { NetworkElementConnection, UpdateNetworkElement, propertyOf } from '../models/networkElementConnection';
-import { removeWebUriAction } from '../actions/commonNetworkElementsActions';
+import { loadAllTlsKeyListAsync } from '../actions/tlsKeyActions';
+import { NetworkElementConnection, propertyOf, UpdateNetworkElement } from '../models/networkElementConnection';
 
 export enum EditNetworkElementDialogMode {
-  None = "none",
-  EditNetworkElement = "editNetworkElement",
-  RemoveNetworkElement = "removeNetworkElement",
-  AddNewNetworkElement = "addNewNetworkElement",
-  MountNetworkElement = "mountNetworkElement",
-  UnmountNetworkElement = "unmountNetworkElement",
+  None = 'none',
+  EditNetworkElement = 'editNetworkElement',
+  RemoveNetworkElement = 'removeNetworkElement',
+  AddNewNetworkElement = 'addNewNetworkElement',
+  MountNetworkElement = 'mountNetworkElement',
+  UnmountNetworkElement = 'unmountNetworkElement',
 }
 
-
-
 const mapDispatch = (dispatcher: IDispatcher) => ({
   addNewNetworkElement: async (element: NetworkElementConnection) => {
     await dispatcher.dispatch(addNewNetworkElementAsyncActionCreator(element));
@@ -63,12 +64,12 @@ const mapDispatch = (dispatcher: IDispatcher) => ({
   editNetworkElement: async (element: UpdateNetworkElement, mountElement: NetworkElementConnection) => {
 
     const values = Object.keys(element);
-    console.log("edit element");
+    console.log('edit element');
     console.log(values);
 
     //make sure properties are there in case they get renamed
-    const idProperty = propertyOf<UpdateNetworkElement>("id");
-    const isRequiredProperty = propertyOf<UpdateNetworkElement>("isRequired");
+    const idProperty = propertyOf<UpdateNetworkElement>('id');
+    const isRequiredProperty = propertyOf<UpdateNetworkElement>('isRequired');
 
 
     if (values.length === 2 && values.includes(idProperty as string) && values.includes(isRequiredProperty as string)) {
@@ -84,92 +85,92 @@ const mapDispatch = (dispatcher: IDispatcher) => ({
     await dispatcher.dispatch(removeNetworkElementAsyncActionCreator(element));
     dispatcher.dispatch(removeWebUriAction(element.id));
   },
-  getAvailableTlsKeys: async () => await dispatcher.dispatch(loadAllTlsKeyListAsync()),
+  getAvailableTlsKeys: async () => dispatcher.dispatch(loadAllTlsKeyListAsync()),
 });
 
 type DialogSettings = {
-  dialogTitle: string,
-  dialogDescription: string,
-  applyButtonText: string,
-  cancelButtonText: string,
-  enableMountIdEditor: boolean,
-  enableUsernameEditor: boolean,
-  enableExtendedEditor: boolean,
-}
+  dialogTitle: string;
+  dialogDescription: string;
+  applyButtonText: string;
+  cancelButtonText: string;
+  enableMountIdEditor: boolean;
+  enableUsernameEditor: boolean;
+  enableExtendedEditor: boolean;
+};
 
 const settings: { [key: string]: DialogSettings } = {
   [EditNetworkElementDialogMode.None]: {
-    dialogTitle: "",
-    dialogDescription: "",
-    applyButtonText: "",
-    cancelButtonText: "",
+    dialogTitle: '',
+    dialogDescription: '',
+    applyButtonText: '',
+    cancelButtonText: '',
     enableMountIdEditor: false,
     enableUsernameEditor: false,
     enableExtendedEditor: false,
   },
 
   [EditNetworkElementDialogMode.AddNewNetworkElement]: {
-    dialogTitle: "Add New Node",
-    dialogDescription: "Add this new node:",
-    applyButtonText: "Add node",
-    cancelButtonText: "Cancel",
+    dialogTitle: 'Add New Node',
+    dialogDescription: 'Add this new node:',
+    applyButtonText: 'Add node',
+    cancelButtonText: 'Cancel',
     enableMountIdEditor: true,
     enableUsernameEditor: true,
     enableExtendedEditor: true,
   },
   [EditNetworkElementDialogMode.MountNetworkElement]: {
-    dialogTitle: "Mount Node",
-    dialogDescription: "Mount this node:",
-    applyButtonText: "Mount node",
-    cancelButtonText: "Cancel",
+    dialogTitle: 'Mount Node',
+    dialogDescription: 'Mount this node:',
+    applyButtonText: 'Mount node',
+    cancelButtonText: 'Cancel',
     enableMountIdEditor: false,
     enableUsernameEditor: false,
     enableExtendedEditor: false,
   },
   [EditNetworkElementDialogMode.UnmountNetworkElement]: {
-    dialogTitle: "Unmount Node",
-    dialogDescription: "Unmount this node:",
-    applyButtonText: "Unmount node",
-    cancelButtonText: "Cancel",
+    dialogTitle: 'Unmount Node',
+    dialogDescription: 'Unmount this node:',
+    applyButtonText: 'Unmount node',
+    cancelButtonText: 'Cancel',
     enableMountIdEditor: false,
     enableUsernameEditor: false,
     enableExtendedEditor: false,
   },
   [EditNetworkElementDialogMode.EditNetworkElement]: {
-    dialogTitle: "Modify Node",
-    dialogDescription: "Modify this node",
-    applyButtonText: "Modify",
-    cancelButtonText: "Cancel",
+    dialogTitle: 'Modify Node',
+    dialogDescription: 'Modify this node',
+    applyButtonText: 'Modify',
+    cancelButtonText: 'Cancel',
     enableMountIdEditor: false,
     enableUsernameEditor: true,
     enableExtendedEditor: false,
   },
   [EditNetworkElementDialogMode.RemoveNetworkElement]: {
-    dialogTitle: "Remove Node",
-    dialogDescription: "Do you really want to remove this node?",
-    applyButtonText: "Remove node",
-    cancelButtonText: "Cancel",
+    dialogTitle: 'Remove Node',
+    dialogDescription: 'Do you really want to remove this node?',
+    applyButtonText: 'Remove node',
+    cancelButtonText: 'Cancel',
     enableMountIdEditor: false,
     enableUsernameEditor: false,
     enableExtendedEditor: false,
-  }
-}
+  },
+};
 
 type EditNetworkElementDialogComponentProps = Connect<undefined, typeof mapDispatch> & {
   mode: EditNetworkElementDialogMode;
   initialNetworkElement: NetworkElementConnection;
   onClose: () => void;
-  radioChecked: string
+  radioChecked: string;
 };
 
 type EditNetworkElementDialogComponentState = NetworkElementConnection & {
-  isNameValid: boolean,
-  isHostSet: boolean,
-  isPasswordSelected: boolean,
-  isTlsSelected: boolean,
-  radioSelected: string,
-  showPasswordTextField: boolean,
-  showTlsDropdown: boolean
+  isNameValid: boolean;
+  isHostSet: boolean;
+  isPasswordSelected: boolean;
+  isTlsSelected: boolean;
+  radioSelected: string;
+  showPasswordTextField: boolean;
+  showTlsDropdown: boolean;
 };
 
 class EditNetworkElementDialogComponent extends React.Component<EditNetworkElementDialogComponentProps, EditNetworkElementDialogComponentState> {
@@ -189,16 +190,17 @@ class EditNetworkElementDialogComponent extends React.Component<EditNetworkEleme
       isTlsSelected: false,
       radioSelected: '',
       showPasswordTextField: true,
-      showTlsDropdown: false
+      showTlsDropdown: false,
     };
   }
+
   public handleRadioChange = (event: any) => {
     this.setState({
       radioSelected: event.target.value,
       showPasswordTextField: event.target.value === 'password',
-      showTlsDropdown: event.target.value === 'tlsKey'
+      showTlsDropdown: event.target.value === 'tlsKey',
     });
-  }
+  };
 
   render(): JSX.Element {
     const setting = settings[this.props.mode];
@@ -215,22 +217,27 @@ class EditNetworkElementDialogComponent extends React.Component<EditNetworkEleme
       showTlsDropdown = true;
     }
 
-    let tlsKeysList = this.props.state.connect.availableTlsKeys ? this.props.state.connect.availableTlsKeys.tlsKeysList ? this.props.state.connect.availableTlsKeys.tlsKeysList : [] : []
+    let tlsKeysList = this.props.state.connect.availableTlsKeys ? this.props.state.connect.availableTlsKeys.tlsKeysList ? this.props.state.connect.availableTlsKeys.tlsKeysList : [] : [];
 
     return (
       <Dialog open={this.props.mode !== EditNetworkElementDialogMode.None}>
-        <DialogTitle id="form-dialog-title" aria-label={`${setting.dialogTitle.replace(/ /g, "-").toLowerCase()}-dialog`}>{setting.dialogTitle}</DialogTitle>
+        <DialogTitle id="form-dialog-title" aria-label={`${setting.dialogTitle.replace(/ /g, '-').toLowerCase()}-dialog`}>{setting.dialogTitle}</DialogTitle>
         <DialogContent>
           <DialogContentText>
             {setting.dialogDescription}
           </DialogContentText>
-          <TextField variant="standard" disabled={!setting.enableMountIdEditor} spellCheck={false} autoFocus margin="dense" id="name" label="Node ID" aria-label="name" type="text" fullWidth value={this.state.nodeId} onChange={(event) => { this.setState({ nodeId: event.target.value }); }} />
+          <TextField variant="standard" disabled={!setting.enableMountIdEditor} spellCheck={false} autoFocus margin="dense"
+            id="name" label="Node ID" aria-label="name" type="text" fullWidth value={this.state.nodeId} onChange={(event) => { this.setState({ nodeId: event.target.value }); }} />
           {!this.state.isNameValid && <Typography variant="body1" color="error">Node ID cannot be empty.</Typography>}
-          <TextField variant="standard" disabled={!setting.enableMountIdEditor} spellCheck={false} margin="dense" id="ipaddress" label="Host/IP address" aria-label="ip adress" type="text" fullWidth value={this.state.host} onChange={(event) => { this.setState({ host: event.target.value }); }} />
+          <TextField variant="standard" disabled={!setting.enableMountIdEditor} spellCheck={false} margin="dense"
+            id="ipaddress" label="Host/IP address" aria-label="ip adress" type="text" fullWidth value={this.state.host} onChange={(event) => { this.setState({ host: event.target.value }); }} />
           {!this.state.isHostSet && <Typography variant="body1" color="error">Host/IP address cannot be empty.</Typography>}
 
-          <TextField variant="standard" disabled={!setting.enableMountIdEditor} spellCheck={false} margin="dense" id="netconfport" label="NETCONF port" aria-label="netconf port" type="number" fullWidth value={this.state.port.toString()} onChange={(event) => { this.setState({ port: +event.target.value }); }} />
-          {setting.enableUsernameEditor && <TextField variant="standard" disabled={!setting.enableUsernameEditor} spellCheck={false} margin="dense" id="username" label="Username" aria-label="username" type="text" fullWidth value={this.state.username} onChange={(event) => { this.setState({ username: event.target.value }); }} /> || null}
+          <TextField variant="standard" disabled={!setting.enableMountIdEditor} spellCheck={false} margin="dense"
+            id="netconfport" label="NETCONF port" aria-label="netconf port" type="number" fullWidth value={this.state.port.toString()}
+            onChange={(event) => { this.setState({ port: +event.target.value }); }} />
+          {setting.enableUsernameEditor && <TextField variant="standard" disabled={!setting.enableUsernameEditor} spellCheck={false} margin="dense"
+            id="username" label="Username" aria-label="username" type="text" fullWidth value={this.state.username} onChange={(event) => { this.setState({ username: event.target.value }); }} /> || null}
 
           {setting.enableUsernameEditor &&
             <RadioGroup row aria-label="password-tls-key" name="password-tls-key" value={radioSelected}
@@ -253,7 +260,7 @@ class EditNetworkElementDialogComponent extends React.Component<EditNetworkEleme
                   id="tlsKey" aria-label="tlsKey" value={this.state.tlsKey} fullWidth // displayEmpty
                   onChange={(event) => { this.setState({ tlsKey: event.target.value as any }); }}
                   inputProps={{ name: 'tlsKey', id: 'tlsKey' }}  >
-                  <MenuItem value={""} disabled >--Select tls-key--</MenuItem>
+                  <MenuItem value={''} disabled >--Select tls-key--</MenuItem>
                   {tlsKeysList.map(tlsKey =>
                     (<MenuItem value={tlsKey.key} key={tlsKey.key} aria-label={tlsKey.key} >{tlsKey.key}</MenuItem>))}
                 </Select>
@@ -283,7 +290,7 @@ class EditNetworkElementDialogComponent extends React.Component<EditNetworkEleme
                 port: this.state.port,
                 username: this.state.username,
                 password: this.state.password,
-                tlsKey: this.state.tlsKey
+                tlsKey: this.state.tlsKey,
               });
             }
             event.preventDefault();
@@ -305,7 +312,7 @@ class EditNetworkElementDialogComponent extends React.Component<EditNetworkEleme
     } catch (err) {
       console.log(err);
     }
-  }
+  };
 
   public componentDidMount() {
     this.renderTlsKeys();
@@ -313,37 +320,36 @@ class EditNetworkElementDialogComponent extends React.Component<EditNetworkEleme
 
   public onRadioSelect = (e: any) => {
     if (e.target.value == 'password') {
-      this.setState({ isPasswordSelected: true, isTlsSelected: false })
+      this.setState({ isPasswordSelected: true, isTlsSelected: false });
     } else if (e.target.value == 'tlsKey') {
-      this.setState({ isPasswordSelected: false, isTlsSelected: true })
+      this.setState({ isPasswordSelected: false, isTlsSelected: true });
     }
   };
 
   private onApply = (element: NetworkElementConnection) => {
-    this.props.onClose && this.props.onClose();
+    if (this.props.onClose) this.props.onClose();
     let updateElement: UpdateNetworkElement = {
-      id: this.state.nodeId
-    }
+      id: this.state.nodeId,
+    };
     if (this.state.isPasswordSelected) {
-      element.tlsKey = ''
-    }
-    else if (this.state.isTlsSelected) { //check here
-      element.password = ''
+      element.tlsKey = '';
+    } else if (this.state.isTlsSelected) { //check here
+      element.password = '';
     }
 
     switch (this.props.mode) {
       case EditNetworkElementDialogMode.AddNewNetworkElement:
-        element && this.props.addNewNetworkElement(element);
+        if (element) this.props.addNewNetworkElement(element);
         this.setState({
           radioSelected: '',
           isPasswordSelected: true,
         });
         break;
       case EditNetworkElementDialogMode.MountNetworkElement:
-        element && this.props.mountNetworkElement(element);
+        if (element) this.props.mountNetworkElement(element);
         break;
       case EditNetworkElementDialogMode.UnmountNetworkElement:
-        element && this.props.unmountNetworkElement(element);
+        if (element) this.props.unmountNetworkElement(element);
         break;
       case EditNetworkElementDialogMode.EditNetworkElement:
         if (this.props.initialNetworkElement.isRequired !== this.state.isRequired)
@@ -358,13 +364,13 @@ class EditNetworkElementDialogComponent extends React.Component<EditNetworkEleme
           updateElement.tlsKey = this.state.tlsKey;
           updateElement.password = '';
         }
-        element && this.props.editNetworkElement(updateElement, element);
+        if (element) this.props.editNetworkElement(updateElement, element);
         this.setState({
-          radioSelected: ''
+          radioSelected: '',
         });
         break;
       case EditNetworkElementDialogMode.RemoveNetworkElement:
-        element && this.props.removeNetworkElement(updateElement);
+        if (element) this.props.removeNetworkElement(updateElement);
         break;
     }
 
@@ -373,10 +379,10 @@ class EditNetworkElementDialogComponent extends React.Component<EditNetworkEleme
   };
 
   private onCancel = () => {
-    this.props.onClose && this.props.onClose();
+    if (this.props.onClose) this.props.onClose();
     this.setState({ password: '', username: '', tlsKey: '', radioSelected: '' });
     this.resetRequieredFields();
-  }
+  };
 
   private resetRequieredFields() {
     this.setState({ isNameValid: true, isHostSet: true });
@@ -402,15 +408,16 @@ class EditNetworkElementDialogComponent extends React.Component<EditNetworkEleme
     return areFieldsValid;
   }
 
-  static getDerivedStateFromProps(props: EditNetworkElementDialogComponentProps, state: EditNetworkElementDialogComponentState & { _initialNetworkElement: NetworkElementConnection }): EditNetworkElementDialogComponentState & { _initialNetworkElement: NetworkElementConnection } {
-    if (props.initialNetworkElement !== state._initialNetworkElement) {
-      state = {
+  static getDerivedStateFromProps(props: EditNetworkElementDialogComponentProps, state: EditNetworkElementDialogComponentState & { initialNetworkElement: NetworkElementConnection }): EditNetworkElementDialogComponentState & { initialNetworkElement: NetworkElementConnection } {
+    let returnState = state;
+    if (props.initialNetworkElement !== state.initialNetworkElement) {
+      returnState = {
         ...state,
         ...props.initialNetworkElement,
-        _initialNetworkElement: props.initialNetworkElement,
+        initialNetworkElement: props.initialNetworkElement,
       };
     }
-    return state;
+    return returnState;
   }
 }
 
index b0c7840..4841b93 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
- import * as React from 'react';
-
- import Button from '@mui/material/Button';
- import Dialog from '@mui/material/Dialog';
- import DialogActions from '@mui/material/DialogActions';
- import DialogTitle from '@mui/material/DialogTitle';
- import { MaterialTable, ColumnType, MaterialTableCtorType } from '../../../../framework/src/components/material-table';
- import { IDispatcher, connect, Connect } from '../../../../framework/src/flux/connect';
- import { NetworkElementConnection } from '../models/networkElementConnection';
- import { AvailableCapabilities } from '../models/yangCapabilitiesType'
- export enum InfoNetworkElementDialogMode {
-   None = "none",
-   InfoNetworkElement = "infoNetworkElement"
- }
- const mapDispatch = (dispatcher: IDispatcher) => ({
- });
- const InfoElementTable = MaterialTable as MaterialTableCtorType<AvailableCapabilities>;
- type DialogSettings = {
-   dialogTitle: string,
-   dialogDescription: string,
-   cancelButtonText: string,
- }
- const settings: { [key: string]: DialogSettings } = {
-   [InfoNetworkElementDialogMode.None]: {
-     dialogTitle: "",
-     dialogDescription: "",
-     cancelButtonText: "",
-   },
-   [InfoNetworkElementDialogMode.InfoNetworkElement]: {
-     dialogTitle: "YANG Capabilities of the Node",
-     dialogDescription: "",
-     cancelButtonText: "OK",
-   }
- }
- type InfoNetworkElementDialogComponentProps = Connect<undefined, typeof mapDispatch> & {
-   mode: InfoNetworkElementDialogMode;
-   initialNetworkElement: NetworkElementConnection;
-   onClose: () => void;
- };
- type InfoNetworkElementDialogComponentState = NetworkElementConnection;
- class InfoNetworkElementDialogComponent extends React.Component<InfoNetworkElementDialogComponentProps, InfoNetworkElementDialogComponentState> {
-   constructor(props: InfoNetworkElementDialogComponentProps) {
-     super(props);
-     this.state = {
-       nodeId: this.props.initialNetworkElement.nodeId,
-       isRequired: false,
-       host: this.props.initialNetworkElement.host,
-       port: this.props.initialNetworkElement.port,
-     };
-   }
-   render(): JSX.Element {
-     const setting = settings[this.props.mode];
-     const availableCapabilities = this.props.state.connect.elementInfo.elementInfo["netconf-node-topology:available-capabilities"]["available-capability"];
-     let yangFeatures = this.props.state.connect.elementFeatureInfo.elementFeatureInfo;
-     let yangCapabilities: AvailableCapabilities[] = [];
-     
-     availableCapabilities.forEach(value => {
-       const capabilty = value.capability;
-       const indexRevision = capabilty.indexOf("revision=");
-       const indexModule = capabilty.indexOf(")", indexRevision);
-       if (indexRevision > 0 && indexModule > 0) {
-         let moduleName = capabilty.substring(indexModule + 1);
-         let ModuleFeaturesList;
-         for(let index = 0; index < yangFeatures.length; index++) {
-           if(yangFeatures[index].name == moduleName) {
-             ModuleFeaturesList = yangFeatures[index].feature? yangFeatures[index].feature : null;
-             break;
-           }
-         }
-         const featuresListCommaSeparated= ModuleFeaturesList? ModuleFeaturesList.toString() : ""
-         let featuresList = featuresListCommaSeparated.replace(',',', ');
-         yangCapabilities.push({
-           module: moduleName,
-           revision: capabilty.substring(indexRevision + 9, indexRevision + 19),
-           features: featuresList 
-          });
-       }
-     });
-     yangCapabilities = yangCapabilities.sort((a,b) => a.module === b.module ? 0 : a.module > b.module ? 1 : -1);
-     return (
-       <>
-         <Dialog open={this.props.mode !== InfoNetworkElementDialogMode.None}  >
-           <DialogTitle id="form-dialog-title">{`${setting.dialogTitle}: "${this.state.nodeId}"`}</DialogTitle>
-           <InfoElementTable stickyHeader isPopup tableId="info-element-table" asynchronus columns={[
-             { property: "module", title: "YANG Capability", type: ColumnType.text, width:900 },
-             {
-               property: "revision", title: "Revision", type: ColumnType.custom, customControl: ({ rowData }) => {
-                 return (
-                   <div>
-                     <a href={`/yang-schema/${rowData.module}/${rowData.revision}`} target="_blank"  > {rowData.revision} </a>
-                   </div>
-                 )
-               }
-             },
-             { property: "features", title: "Features", type: ColumnType.text, width:500 },
-           ]} idProperty="id" rows={yangCapabilities}  >
-           </InfoElementTable>
-           <DialogActions>
-             <Button aria-label="ok-button" onClick={(event) => {
-               this.onCancel();
-               event.preventDefault();
-               event.stopPropagation();
-             }} color="secondary"> {setting.cancelButtonText} </Button>
-           </DialogActions>
-         </Dialog>
-       </>
-     )
-   }
-   private onCancel = () => {
-     this.props.onClose();
-   }
-   static getDerivedStateFromProps(props: InfoNetworkElementDialogComponentProps, state: InfoNetworkElementDialogComponentState & { _initialNetworkElement: NetworkElementConnection }): InfoNetworkElementDialogComponentState & { _initialNetworkElement: NetworkElementConnection } {
-     if (props.initialNetworkElement !== state._initialNetworkElement) {
-       state = {
-         ...state,
-         ...props.initialNetworkElement,
-         _initialNetworkElement: props.initialNetworkElement,
-       };
-     }
-     return state;
-   }
- }
- export const InfoNetworkElementDialog = connect(undefined, mapDispatch)(InfoNetworkElementDialogComponent);
- export default InfoNetworkElementDialog;
\ No newline at end of file
+import * as React from 'react';
+
+import Button from '@mui/material/Button';
+import Dialog from '@mui/material/Dialog';
+import DialogActions from '@mui/material/DialogActions';
+import DialogTitle from '@mui/material/DialogTitle';
+
+import { ColumnType, MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table';
+import { connect, Connect } from '../../../../framework/src/flux/connect';
+
+import { NetworkElementConnection } from '../models/networkElementConnection';
+import { AvailableCapabilities } from '../models/yangCapabilitiesType';
+
+export enum InfoNetworkElementDialogMode {
+  None = 'none',
+  InfoNetworkElement = 'infoNetworkElement',
+}
+
+const mapDispatch = () => ({
+});
+
+const InfoElementTable = MaterialTable as MaterialTableCtorType<AvailableCapabilities>;
+
+type DialogSettings = {
+  dialogTitle: string;
+  dialogDescription: string;
+  cancelButtonText: string;
+};
+
+const settings: { [key: string]: DialogSettings } = {
+  [InfoNetworkElementDialogMode.None]: {
+    dialogTitle: '',
+    dialogDescription: '',
+    cancelButtonText: '',
+  },
+  [InfoNetworkElementDialogMode.InfoNetworkElement]: {
+    dialogTitle: 'YANG Capabilities of the Node',
+    dialogDescription: '',
+    cancelButtonText: 'OK',
+  },
+};
+
+type InfoNetworkElementDialogComponentProps = Connect<undefined, typeof mapDispatch> & {
+  mode: InfoNetworkElementDialogMode;
+  initialNetworkElement: NetworkElementConnection;
+  onClose: () => void;
+};
+
+type InfoNetworkElementDialogComponentState = NetworkElementConnection;
+
+class InfoNetworkElementDialogComponent extends React.Component<InfoNetworkElementDialogComponentProps, InfoNetworkElementDialogComponentState> {
+  constructor(props: InfoNetworkElementDialogComponentProps) {
+    super(props);
+
+    this.state = {
+      nodeId: this.props.initialNetworkElement.nodeId,
+      isRequired: false,
+      host: this.props.initialNetworkElement.host,
+      port: this.props.initialNetworkElement.port,
+    };
+  }
+
+  render(): JSX.Element {
+    const setting = settings[this.props.mode];
+    const availableCapabilities = this.props.state.connect.elementInfo.elementInfo['netconf-node-topology:available-capabilities']['available-capability'];
+    let yangFeatures = this.props.state.connect.elementFeatureInfo.elementFeatureInfo;
+    let yangCapabilities: AvailableCapabilities[] = [];
+
+    availableCapabilities.forEach(value => {
+      const capabilty = value.capability;
+      const indexRevision = capabilty.indexOf('revision=');
+      const indexModule = capabilty.indexOf(')', indexRevision);
+      if (indexRevision > 0 && indexModule > 0) {
+        let moduleName = capabilty.substring(indexModule + 1);
+        let ModuleFeaturesList;
+        for (let index = 0; index < yangFeatures.length; index++) {
+          if (yangFeatures[index].name == moduleName) {
+            ModuleFeaturesList = yangFeatures[index].feature ? yangFeatures[index].feature : null;
+            break;
+          }
+        }
+        const featuresListCommaSeparated = ModuleFeaturesList ? ModuleFeaturesList.toString() : '';
+        let featuresList = featuresListCommaSeparated.replace(',', ', ');
+
+        yangCapabilities.push({
+          module: moduleName,
+          revision: capabilty.substring(indexRevision + 9, indexRevision + 19),
+          features: featuresList,
+        });
+      }
+    });
+
+    yangCapabilities = yangCapabilities.sort((a, b) => a.module === b.module ? 0 : a.module > b.module ? 1 : -1);
+
+    return (
+      <>
+        <Dialog open={this.props.mode !== InfoNetworkElementDialogMode.None}  >
+          <DialogTitle id="form-dialog-title">{`${setting.dialogTitle}: "${this.state.nodeId}"`}</DialogTitle>
+          <InfoElementTable stickyHeader isPopup tableId="info-element-table" asynchronus columns={[
+            { property: 'module', title: 'YANG Capability', type: ColumnType.text, width: 900 },
+            {
+              property: 'revision', title: 'Revision', type: ColumnType.custom, customControl: ({ rowData }) => {
+                return (
+                  <div>
+                    <a href={`/yang-schema/${rowData.module}/${rowData.revision}`} target="_blank"  > {rowData.revision} </a>
+                  </div>
+                );
+              },
+            },
+            { property: 'features', title: 'Features', type: ColumnType.text, width: 500 },
+          ]} idProperty="id" rows={yangCapabilities}  >
+          </InfoElementTable>
+          <DialogActions>
+            <Button aria-label="ok-button" onClick={(event) => {
+              this.onCancel();
+              event.preventDefault();
+              event.stopPropagation();
+            }} color="secondary"> {setting.cancelButtonText} </Button>
+          </DialogActions>
+        </Dialog>
+      </>
+    );
+  }
+
+  private onCancel = () => {
+    this.props.onClose();
+  };
+
+  static getDerivedStateFromProps(props: InfoNetworkElementDialogComponentProps, state: InfoNetworkElementDialogComponentState & { initialNetworkElement: NetworkElementConnection }): InfoNetworkElementDialogComponentState & { initialNetworkElement: NetworkElementConnection } {
+    let returnState = state;
+    if (props.initialNetworkElement !== state.initialNetworkElement) {
+      returnState = {
+        ...state,
+        ...props.initialNetworkElement,
+        initialNetworkElement: props.initialNetworkElement,
+      };
+    }
+    return returnState;
+  }
+}
+
+export const InfoNetworkElementDialog = connect(undefined, mapDispatch)(InfoNetworkElementDialogComponent);
+export default InfoNetworkElementDialog;
\ No newline at end of file
index 67fdef6..1ce8f0c 100644 (file)
 * the License.
 * ============LICENSE_END==========================================================================
 */
-import * as React from 'react';
-import { Theme } from '@mui/material/styles';
-
-import { WithStyles } from '@mui/styles';
-import createStyles from '@mui/styles/createStyles';
-import withStyles from '@mui/styles/withStyles';
+import React from 'react';
 
 import AddIcon from '@mui/icons-material/Add';
-import Refresh from '@mui/icons-material/Refresh';
+import ComputerIcon from '@mui/icons-material/Computer';
+import EditIcon from '@mui/icons-material/Edit';
+import Info from '@mui/icons-material/Info';
 import LinkIcon from '@mui/icons-material/Link';
 import LinkOffIcon from '@mui/icons-material/LinkOff';
+import Refresh from '@mui/icons-material/Refresh';
 import RemoveIcon from '@mui/icons-material/RemoveCircleOutline';
-import EditIcon from '@mui/icons-material/Edit';
-import Info from '@mui/icons-material/Info';
-import ComputerIcon from '@mui/icons-material/Computer';
-import { MenuItem, Divider, Typography } from '@mui/material';
+import { Divider, MenuItem, Typography } from '@mui/material';
+import { Theme } from '@mui/material/styles';
+import { WithStyles } from '@mui/styles';
+import createStyles from '@mui/styles/createStyles';
+import withStyles from '@mui/styles/withStyles';
 
-import { MaterialTable, ColumnType, MaterialTableCtorType } from '../../../../framework/src/components/material-table';
-import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
-import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect';
 import { NavigateToApplication } from '../../../../framework/src/actions/navigationActions';
+import { ColumnType, MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table';
+import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect';
+import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
+import { getAccessPolicyByUrl } from '../../../../framework/src/services/restService';
 
+import { loadAllInfoElementAsync, loadAllInfoElementFeaturesAsync } from '../actions/infoNetworkElementActions';
 import { createNetworkElementsActions, createNetworkElementsProperties } from '../handlers/networkElementsHandler';
-
 import { NetworkElementConnection } from '../models/networkElementConnection';
 import { ModuleSet, TopologyNode } from '../models/topologyNetconf';
-import EditNetworkElementDialog, { EditNetworkElementDialogMode } from './editNetworkElementDialog';
-import RefreshNetworkElementsDialog, { RefreshNetworkElementsDialogMode } from './refreshNetworkElementsDialog';
+import { connectService } from '../services/connectService';
 
+import EditNetworkElementDialog, { EditNetworkElementDialogMode } from './editNetworkElementDialog';
 import InfoNetworkElementDialog, { InfoNetworkElementDialogMode } from './infoNetworkElementDialog';
-import { loadAllInfoElementAsync, loadAllInfoElementFeaturesAsync } from '../actions/infoNetworkElementActions';
-import { connectService } from '../services/connectService';
-import { getAccessPolicyByUrl } from '../../../../framework/src/services/restService';
+import RefreshNetworkElementsDialog, { RefreshNetworkElementsDialogMode } from './refreshNetworkElementsDialog';
 
 const styles = (theme: Theme) => createStyles({
   connectionStatusConnected: {
@@ -61,17 +59,17 @@ const styles = (theme: Theme) => createStyles({
   },
   button: {
     margin: 0,
-    padding: "6px 6px",
-    minWidth: 'unset'
+    padding: '6px 6px',
+    minWidth: 'unset',
   },
   spacer: {
     marginLeft: theme.spacing(1),
     marginRight: theme.spacing(1),
-    display: "inline"
-  }
+    display: 'inline',
+  },
 });
 
-type GetStatelessComponentProps<T> = T extends (props: infer P & { children?: React.ReactNode }) => any ? P : any
+type GetStatelessComponentProps<T> = T extends (props: infer P & { children?: React.ReactNode }) => any ? P : any;
 const MenuItemExt: React.FC<GetStatelessComponentProps<typeof MenuItem>> = (props) => {
   const [disabled, setDisabled] = React.useState(true);
   const onMouseDown = (ev: React.MouseEvent<HTMLElement>) => {
@@ -95,21 +93,21 @@ const mapProps = (state: IApplicationStoreState) => ({
 const mapDispatch = (dispatcher: IDispatcher) => ({
   networkElementsActions: createNetworkElementsActions(dispatcher.dispatch),
   navigateToApplication: (applicationName: string, path?: string) => dispatcher.dispatch(new NavigateToApplication(applicationName, path)),
-  networkElementInfo: async (nodeId: string) => await dispatcher.dispatch(loadAllInfoElementAsync(nodeId)),
-  networkElementFeaturesInfo: async (nodeId: string) => await dispatcher.dispatch(loadAllInfoElementFeaturesAsync(nodeId))
+  networkElementInfo: async (nodeId: string) => dispatcher.dispatch(loadAllInfoElementAsync(nodeId)),
+  networkElementFeaturesInfo: async (nodeId: string) => dispatcher.dispatch(loadAllInfoElementFeaturesAsync(nodeId)),
 });
 
 type NetworkElementsListComponentProps = WithStyles<typeof styles> & Connect<typeof mapProps, typeof mapDispatch>;
 type NetworkElementsListComponentState = {
-  networkElementToEdit: NetworkElementConnection,
-  networkElementEditorMode: EditNetworkElementDialogMode,
-  refreshNetworkElementsEditorMode: RefreshNetworkElementsDialogMode,
-  infoNetworkElementEditorMode: InfoNetworkElementDialogMode,
-  elementInfo: TopologyNode | null,
-  elementInfoFeature: ModuleSet | null
-}
+  networkElementToEdit: NetworkElementConnection;
+  networkElementEditorMode: EditNetworkElementDialogMode;
+  refreshNetworkElementsEditorMode: RefreshNetworkElementsDialogMode;
+  infoNetworkElementEditorMode: InfoNetworkElementDialogMode;
+  elementInfo: TopologyNode | null;
+  elementInfoFeature: ModuleSet | null;
+};
 
-const emptyRequireNetworkElement: NetworkElementConnection = { id: "", nodeId: "", host: "", port: 830, status: "Disconnected", isRequired: true };
+const emptyRequireNetworkElement: NetworkElementConnection = { id: '', nodeId: '', host: '', port: 830, status: 'Disconnected', isRequired: true };
 let initialSorted = false;
 const NetworkElementTable = MaterialTable as MaterialTableCtorType<NetworkElementConnection>;
 
@@ -124,7 +122,7 @@ export class NetworkElementsListComponent extends React.Component<NetworkElement
       refreshNetworkElementsEditorMode: RefreshNetworkElementsDialogMode.None,
       elementInfo: null,
       elementInfoFeature: null,
-      infoNetworkElementEditorMode: InfoNetworkElementDialogMode.None
+      infoNetworkElementEditorMode: InfoNetworkElementDialogMode.None,
     };
   }
 
@@ -135,25 +133,25 @@ export class NetworkElementsListComponent extends React.Component<NetworkElement
 
     const { configuration } = this.props.applicationState as any;
     const buttonArray = [
-      <MenuItemExt aria-label={"mount-button"} onClick={event => this.onOpenMountdNetworkElementsDialog(event, rowData)} disabled={!canMount} ><LinkIcon /><Typography>Mount</Typography></MenuItemExt>,
-      <MenuItemExt aria-label={"unmount-button"} onClick={event => this.onOpenUnmountdNetworkElementsDialog(event, rowData)} disabled={!canMount} ><LinkOffIcon /><Typography>Unmount</Typography></MenuItemExt>,
+      <MenuItemExt aria-label={'mount-button'} onClick={event => this.onOpenMountdNetworkElementsDialog(event, rowData)} disabled={!canMount} ><LinkIcon /><Typography>Mount</Typography></MenuItemExt>,
+      <MenuItemExt aria-label={'unmount-button'} onClick={event => this.onOpenUnmountdNetworkElementsDialog(event, rowData)} disabled={!canMount} ><LinkOffIcon /><Typography>Unmount</Typography></MenuItemExt>,
       <Divider />,
-      <MenuItem aria-label={"info-button"} onClick={event => this.onOpenInfoNetworkElementDialog(event, rowData)} disabled={rowData.status !== "Connected"} ><Info /><Typography>Info</Typography></MenuItem>,
-      <MenuItem aria-label={"edit-button"} onClick={event => this.onOpenEditNetworkElementDialog(event, rowData)}><EditIcon /><Typography>Edit</Typography></MenuItem>,
-      <MenuItem aria-label={"remove-button"} onClick={event => this.onOpenRemoveNetworkElementDialog(event, rowData)} ><RemoveIcon /><Typography>Remove</Typography></MenuItem>,
+      <MenuItem aria-label={'info-button'} onClick={event => this.onOpenInfoNetworkElementDialog(event, rowData)} disabled={rowData.status !== 'Connected'} ><Info /><Typography>Info</Typography></MenuItem>,
+      <MenuItem aria-label={'edit-button'} onClick={event => this.onOpenEditNetworkElementDialog(event, rowData)}><EditIcon /><Typography>Edit</Typography></MenuItem>,
+      <MenuItem aria-label={'remove-button'} onClick={event => this.onOpenRemoveNetworkElementDialog(event, rowData)} ><RemoveIcon /><Typography>Remove</Typography></MenuItem>,
       <Divider />,
-      <MenuItem aria-label={"inventory-button"} onClick={event => this.props.navigateToApplication("inventory", rowData.nodeId)}><Typography>Inventory</Typography></MenuItem>,
+      <MenuItem aria-label={'inventory-button'} onClick={() => this.props.navigateToApplication('inventory', rowData.nodeId)}><Typography>Inventory</Typography></MenuItem>,
       <Divider />,
-      <MenuItem aria-label={"fault-button"} onClick={event => this.props.navigateToApplication("fault", rowData.nodeId)} ><Typography>Fault</Typography></MenuItem>,
-      <MenuItem aria-label={"configure-button"} onClick={event => this.props.navigateToApplication("configuration", rowData.nodeId)} disabled={rowData.status === "Connecting" || rowData.status === "Disconnected" || !configuration}><Typography>Configure</Typography></MenuItem>,
-      <MenuItem onClick={event => this.props.navigateToApplication("accounting", rowData.nodeId)} disabled={true}><Typography>Accounting</Typography></MenuItem>,
-      <MenuItem aria-label={"performance-button"} onClick={event => this.props.navigateToApplication("performanceHistory", rowData.nodeId)}><Typography>Performance</Typography></MenuItem>,
-      <MenuItem onClick={event => this.props.navigateToApplication("security", rowData.nodeId)} disabled={true} ><Typography>Security</Typography></MenuItem>,
+      <MenuItem aria-label={'fault-button'} onClick={() => this.props.navigateToApplication('fault', rowData.nodeId)} ><Typography>Fault</Typography></MenuItem>,
+      <MenuItem aria-label={'configure-button'} onClick={() => this.props.navigateToApplication('configuration', rowData.nodeId)} disabled={rowData.status === 'Connecting' || rowData.status === 'Disconnected' || !configuration}><Typography>Configure</Typography></MenuItem>,
+      <MenuItem onClick={() => this.props.navigateToApplication('accounting', rowData.nodeId)} disabled={true}><Typography>Accounting</Typography></MenuItem>,
+      <MenuItem aria-label={'performance-button'} onClick={() => this.props.navigateToApplication('performanceHistory', rowData.nodeId)}><Typography>Performance</Typography></MenuItem>,
+      <MenuItem onClick={() => this.props.navigateToApplication('security', rowData.nodeId)} disabled={true} ><Typography>Security</Typography></MenuItem>,
     ];
 
     if (rowData.weburi) {
       // add an icon for gui cuttrough, if weburi is available
-      return [<MenuItem aria-label={"web-client-button"} onClick={event => window.open(rowData.weburi, "_blank")} ><ComputerIcon /><Typography>Web Client</Typography></MenuItem>].concat(buttonArray)
+      return [<MenuItem aria-label={'web-client-button'} onClick={() => window.open(rowData.weburi, '_blank')} ><ComputerIcon /><Typography>Web Client</Typography></MenuItem>].concat(buttonArray);
     } else {
       return buttonArray;
     }
@@ -162,13 +160,13 @@ export class NetworkElementsListComponent extends React.Component<NetworkElement
   //  private navigationCreator
 
   render(): JSX.Element {
-    const { classes } = this.props;
+    //const { classes } = this.props;
     const { networkElementToEdit } = this.state;
-    let savedRadio = "password";
+    let savedRadio = 'password';
     if (this.state.networkElementToEdit.password && this.state.networkElementToEdit.password.length > 0) {
-      savedRadio = 'password'
+      savedRadio = 'password';
     } else if (this.state.networkElementToEdit.tlsKey && this.state.networkElementToEdit.tlsKey.length > 0) {
-      savedRadio = 'tlsKey'
+      savedRadio = 'tlsKey';
     }
 
     // const mountUri = rowData.id && connectService.getNetworkElementUri(rowData.id);
@@ -177,32 +175,32 @@ export class NetworkElementsListComponent extends React.Component<NetworkElement
     const canAdd = true;
 
     const addRequireNetworkElementAction = {
-      icon: AddIcon, tooltip: 'Add node', ariaLabel: "add-element", onClick: () => {
+      icon: AddIcon, tooltip: 'Add node', ariaLabel: 'add-element', onClick: () => {
         this.setState({
           networkElementEditorMode: EditNetworkElementDialogMode.AddNewNetworkElement,
           networkElementToEdit: emptyRequireNetworkElement,
         });
-      }
+      },
     };
 
     const refreshNetworkElementsAction = {
       icon: Refresh, tooltip: 'Refresh table', ariaLabel: 'refresh', onClick: () => {
         this.setState({
-          refreshNetworkElementsEditorMode: RefreshNetworkElementsDialogMode.RefreshNetworkElementsTable
+          refreshNetworkElementsEditorMode: RefreshNetworkElementsDialogMode.RefreshNetworkElementsTable,
         });
-      }
+      },
     };
 
     return <>
       <NetworkElementTable stickyHeader tableId="network-element-table" customActionButtons={[refreshNetworkElementsAction, ...(canAdd ? [addRequireNetworkElementAction] : [])]} columns={[
-        { property: "nodeId", title: "Node ID", type: ColumnType.text },
-        { property: "status", title: "Connection Status", type: ColumnType.text, width:'15%' },
-        { property: "host", title: "Host", type: ColumnType.text },
-        { property: "port", title: "Port", type: ColumnType.numeric },
-        { property: "isRequired", title: "Required", type: ColumnType.boolean },
-        { property: "deviceType", title: "Type", type: ColumnType.text },
-      //  { property: "coreModelCapability", title: "Core Model", type: ColumnType.text },
-        { property: "deviceFunction", title: "Function", type: ColumnType.text, width: '25%' }
+        { property: 'nodeId', title: 'Node ID', type: ColumnType.text },
+        { property: 'status', title: 'Connection Status', type: ColumnType.text, width:'15%' },
+        { property: 'host', title: 'Host', type: ColumnType.text },
+        { property: 'port', title: 'Port', type: ColumnType.numeric },
+        { property: 'isRequired', title: 'Required', type: ColumnType.boolean },
+        { property: 'deviceType', title: 'Type', type: ColumnType.text },
+        //  { property: "coreModelCapability", title: "Core Model", type: ColumnType.text },
+        { property: 'deviceFunction', title: 'Function', type: ColumnType.text, width: '25%' },
       ]} idProperty="id" {...this.props.networkElementsActions} {...this.props.networkElementsProperties} asynchronus createContextMenu={rowData => {
 
         return this.getContextMenu(rowData);
@@ -224,12 +222,12 @@ export class NetworkElementsListComponent extends React.Component<NetworkElement
         onClose={this.onCloseInfoNetworkElementDialog}
       />
     </>;
-  };
+  }
 
   public componentDidMount() {
     if (!initialSorted) {
       initialSorted = true;
-      this.props.networkElementsActions.onHandleRequestSort("node-id");
+      this.props.networkElementsActions.onHandleRequestSort('node-id');
     } else {
       this.props.networkElementsActions.onRefresh();
     }
@@ -238,23 +236,23 @@ export class NetworkElementsListComponent extends React.Component<NetworkElement
   private onOpenAddNetworkElementDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => {
     this.setState({
       networkElementToEdit: element,
-      networkElementEditorMode: EditNetworkElementDialogMode.AddNewNetworkElement
+      networkElementEditorMode: EditNetworkElementDialogMode.AddNewNetworkElement,
     });
-  }
+  };
 
   private onOpenRemoveNetworkElementDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => {
     this.setState({
       networkElementToEdit: element,
-      networkElementEditorMode: EditNetworkElementDialogMode.RemoveNetworkElement
+      networkElementEditorMode: EditNetworkElementDialogMode.RemoveNetworkElement,
     });
-  }
+  };
 
   private onOpenEditNetworkElementDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => {
-    let radioSaved;
-    if (element.password && element.password.length > 0)
-      radioSaved = 'password'
-    else if (element.tlsKey && element.tlsKey.length > 0)
-      radioSaved = 'tlsKey'
+    //let radioSaved;
+    //if (element.password && element.password.length > 0)
+    //  radioSaved = 'password';
+    //else if (element.tlsKey && element.tlsKey.length > 0)
+    //  radioSaved = 'tlsKey';
     this.setState({
       networkElementToEdit: {
         nodeId: element.nodeId,
@@ -263,25 +261,25 @@ export class NetworkElementsListComponent extends React.Component<NetworkElement
         port: element.port,
         username: element.username,
         password: element.password,
-        tlsKey: element.tlsKey
+        tlsKey: element.tlsKey,
       },
-      networkElementEditorMode: EditNetworkElementDialogMode.EditNetworkElement
+      networkElementEditorMode: EditNetworkElementDialogMode.EditNetworkElement,
     });
-  }
+  };
 
   private onOpenUnmountdNetworkElementsDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => {
     this.setState({
       networkElementToEdit: element,
-      networkElementEditorMode: EditNetworkElementDialogMode.UnmountNetworkElement
+      networkElementEditorMode: EditNetworkElementDialogMode.UnmountNetworkElement,
     });
-  }
+  };
 
   private onOpenMountdNetworkElementsDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => {
     this.setState({
       networkElementToEdit: element,
-      networkElementEditorMode: EditNetworkElementDialogMode.MountNetworkElement
+      networkElementEditorMode: EditNetworkElementDialogMode.MountNetworkElement,
     });
-  }
+  };
 
   private onOpenInfoNetworkElementDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => {
     this.props.networkElementInfo(element.nodeId);
@@ -290,25 +288,27 @@ export class NetworkElementsListComponent extends React.Component<NetworkElement
       networkElementToEdit: element,
       infoNetworkElementEditorMode: InfoNetworkElementDialogMode.InfoNetworkElement,
     });
-  }
+  };
 
   private onCloseEditNetworkElementDialog = () => {
     this.setState({
       networkElementEditorMode: EditNetworkElementDialogMode.None,
       networkElementToEdit: emptyRequireNetworkElement,
     });
-  }
+  };
+
   private onCloseInfoNetworkElementDialog = () => {
     this.setState({
       infoNetworkElementEditorMode: InfoNetworkElementDialogMode.None,
       networkElementToEdit: emptyRequireNetworkElement,
     });
-  }
+  };
+
   private onCloseRefreshNetworkElementsDialog = () => {
     this.setState({
-      refreshNetworkElementsEditorMode: RefreshNetworkElementsDialogMode.None
+      refreshNetworkElementsEditorMode: RefreshNetworkElementsDialogMode.None,
     });
-  }
+  };
 }
 
 export const NetworkElementsList = withStyles(styles)(connect(mapProps, mapDispatch)(NetworkElementsListComponent));
index c09f59b..a4aea7f 100644 (file)
@@ -15,7 +15,7 @@
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
+import React from 'react';
 
 import Button from '@mui/material/Button';
 import Dialog from '@mui/material/Dialog';
@@ -24,78 +24,75 @@ import DialogContent from '@mui/material/DialogContent';
 import DialogContentText from '@mui/material/DialogContentText';
 import DialogTitle from '@mui/material/DialogTitle';
 
-import { connectionStatusLogReloadAction } from '../handlers/connectionStatusLogHandler';
-import { IDispatcher, connect, Connect } from '../../../../framework/src/flux/connect';
+import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect';
 
+import { connectionStatusLogReloadAction } from '../handlers/connectionStatusLogHandler';
 import { ConnectionStatusLogType } from '../models/connectionStatusLog';
 
 export enum RefreshConnectionStatusLogDialogMode {
-  None = "none",
-  RefreshConnectionStatusLogTable = "RefreshConnectionStatusLogTable",
+  None = 'none',
+  RefreshConnectionStatusLogTable = 'RefreshConnectionStatusLogTable',
 }
 
 const mapDispatch = (dispatcher: IDispatcher) => ({
-  refreshConnectionStatusLog: () => dispatcher.dispatch(connectionStatusLogReloadAction)
+  refreshConnectionStatusLog: () => dispatcher.dispatch(connectionStatusLogReloadAction),
 });
 
 type DialogSettings = {
-  dialogTitle: string,
-  dialogDescription: string,
-  applyButtonText: string,
-  cancelButtonText: string,
-  enableMountIdEditor: boolean,
-  enableUsernameEditor: boolean,
-  enableExtendedEditor: boolean,
-}
+  dialogTitle: string;
+  dialogDescription: string;
+  applyButtonText: string;
+  cancelButtonText: string;
+  enableMountIdEditor: boolean;
+  enableUsernameEditor: boolean;
+  enableExtendedEditor: boolean;
+};
 
 const settings: { [key: string]: DialogSettings } = {
   [RefreshConnectionStatusLogDialogMode.None]: {
-    dialogTitle: "",
-    dialogDescription: "",
-    applyButtonText: "",
-    cancelButtonText: "",
+    dialogTitle: '',
+    dialogDescription: '',
+    applyButtonText: '',
+    cancelButtonText: '',
     enableMountIdEditor: false,
     enableUsernameEditor: false,
     enableExtendedEditor: false,
   },
   [RefreshConnectionStatusLogDialogMode.RefreshConnectionStatusLogTable]: {
-    dialogTitle: "Do you want to refresh the Connection Status Log table?",
-    dialogDescription: "",
-    applyButtonText: "Yes",
-    cancelButtonText: "Cancel",
+    dialogTitle: 'Do you want to refresh the Connection Status Log table?',
+    dialogDescription: '',
+    applyButtonText: 'Yes',
+    cancelButtonText: 'Cancel',
     enableMountIdEditor: true,
     enableUsernameEditor: true,
     enableExtendedEditor: true,
-  }
-}
+  },
+};
 
 type RefreshConnectionStatusLogDialogComponentProps = Connect<undefined, typeof mapDispatch> & {
   mode: RefreshConnectionStatusLogDialogMode;
   onClose: () => void;
 };
 
-type RefreshConnectionStatusLogDialogComponentState = ConnectionStatusLogType & { isNameValid: boolean, isHostSet: boolean };
+type RefreshConnectionStatusLogDialogComponentState = ConnectionStatusLogType & { isNameValid: boolean; isHostSet: boolean };
 
 class RefreshConnectionStatusLogDialogComponent extends React.Component<RefreshConnectionStatusLogDialogComponentProps, RefreshConnectionStatusLogDialogComponentState> {
-  constructor(props: RefreshConnectionStatusLogDialogComponentProps) {
-    super(props);
-  }
 
   render(): JSX.Element {
     const setting = settings[this.props.mode];
     return (
       <Dialog open={this.props.mode !== RefreshConnectionStatusLogDialogMode.None}>
-        <DialogTitle id="form-dialog-title" aria-label={`${setting.dialogTitle.replace(/ /g, "-").toLowerCase()}-dialog`}>{setting.dialogTitle}</DialogTitle>
+        <DialogTitle id="form-dialog-title" aria-label={`${setting.dialogTitle.replace(/ /g, '-').toLowerCase()}-dialog`}>{setting.dialogTitle}</DialogTitle>
         <DialogContent>
           <DialogContentText>
             {setting.dialogDescription}
           </DialogContentText>
         </DialogContent>
         <DialogActions>
-          <Button aria-label="dialog-confirm-button" onClick={(event) => {
+          <Button aria-label="dialog-confirm-button" onClick={() => {
             this.onRefresh();
           }} color="inherit" > {setting.applyButtonText} </Button>
-          <Button aria-label="dialog-cancel-button" onClick={(event) => {
+          <Button aria-label="dialog-cancel-button" onClick={() => {
             this.onCancel();
           }} color="secondary"> {setting.cancelButtonText} </Button>
         </DialogActions>
@@ -110,7 +107,7 @@ class RefreshConnectionStatusLogDialogComponent extends React.Component<RefreshC
 
   private onCancel = () => {
     this.props.onClose();
-  }
+  };
 }
 
 export const RefreshConnectionStatusLogDialog = connect(undefined, mapDispatch)(RefreshConnectionStatusLogDialogComponent);
index abf5938..e41fd27 100644 (file)
@@ -15,7 +15,7 @@
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
+import React from 'react';
 
 import Button from '@mui/material/Button';
 import Dialog from '@mui/material/Dialog';
@@ -24,78 +24,75 @@ import DialogContent from '@mui/material/DialogContent';
 import DialogContentText from '@mui/material/DialogContentText';
 import DialogTitle from '@mui/material/DialogTitle';
 
-import { networkElementsReloadAction } from '../handlers/networkElementsHandler';
-import { IDispatcher, connect, Connect } from '../../../../framework/src/flux/connect';
+import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect';
 
+import { networkElementsReloadAction } from '../handlers/networkElementsHandler';
 import { NetworkElementConnection } from '../models/networkElementConnection';
 
 export enum RefreshNetworkElementsDialogMode {
-  None = "none",
-  RefreshNetworkElementsTable = "RefreshNetworkElementsTable",
+  None = 'none',
+  RefreshNetworkElementsTable = 'RefreshNetworkElementsTable',
 }
 
 const mapDispatch = (dispatcher: IDispatcher) => ({
-  refreshNetworkElement: () => dispatcher.dispatch(networkElementsReloadAction)
+  refreshNetworkElement: () => dispatcher.dispatch(networkElementsReloadAction),
 });
 
 type DialogSettings = {
-  dialogTitle: string,
-  dialogDescription: string,
-  applyButtonText: string,
-  cancelButtonText: string,
-  enableMountIdEditor: boolean,
-  enableUsernameEditor: boolean,
-  enableExtendedEditor: boolean,
-}
+  dialogTitle: string;
+  dialogDescription: string;
+  applyButtonText: string;
+  cancelButtonText: string;
+  enableMountIdEditor: boolean;
+  enableUsernameEditor: boolean;
+  enableExtendedEditor: boolean;
+};
 
 const settings: { [key: string]: DialogSettings } = {
   [RefreshNetworkElementsDialogMode.None]: {
-    dialogTitle: "",
-    dialogDescription: "",
-    applyButtonText: "",
-    cancelButtonText: "",
+    dialogTitle: '',
+    dialogDescription: '',
+    applyButtonText: '',
+    cancelButtonText: '',
     enableMountIdEditor: false,
     enableUsernameEditor: false,
     enableExtendedEditor: false,
   },
   [RefreshNetworkElementsDialogMode.RefreshNetworkElementsTable]: {
-    dialogTitle: "Do you want to refresh the nodes table?",
-    dialogDescription: "",
-    applyButtonText: "Yes",
-    cancelButtonText: "Cancel",
+    dialogTitle: 'Do you want to refresh the nodes table?',
+    dialogDescription: '',
+    applyButtonText: 'Yes',
+    cancelButtonText: 'Cancel',
     enableMountIdEditor: true,
     enableUsernameEditor: true,
     enableExtendedEditor: true,
-  }
-}
+  },
+};
 
 type RefreshNetworkElementsDialogComponentProps = Connect<undefined, typeof mapDispatch> & {
   mode: RefreshNetworkElementsDialogMode;
   onClose: () => void;
 };
 
-type RefreshNetworkElementsDialogComponentState = NetworkElementConnection & { isNameValid: boolean, isHostSet: boolean };
+type RefreshNetworkElementsDialogComponentState = NetworkElementConnection & { isNameValid: boolean; isHostSet: boolean };
 
 class RefreshNetworkElementsDialogComponent extends React.Component<RefreshNetworkElementsDialogComponentProps, RefreshNetworkElementsDialogComponentState> {
-  constructor(props: RefreshNetworkElementsDialogComponentProps) {
-    super(props);
-  }
 
   render(): JSX.Element {
     const setting = settings[this.props.mode];
     return (
       <Dialog open={this.props.mode !== RefreshNetworkElementsDialogMode.None}>
-        <DialogTitle id="form-dialog-title" aria-label={`${setting.dialogTitle.replace(/ /g, "-").toLowerCase()}-dialog`}>{setting.dialogTitle}</DialogTitle>
+        <DialogTitle id="form-dialog-title" aria-label={`${setting.dialogTitle.replace(/ /g, '-').toLowerCase()}-dialog`}>{setting.dialogTitle}</DialogTitle>
         <DialogContent>
           <DialogContentText>
             {setting.dialogDescription}
           </DialogContentText>
         </DialogContent>
         <DialogActions>
-          <Button aria-label="dialog-confirm-button" onClick={(event) => {
+          <Button aria-label="dialog-confirm-button" onClick={() => {
             this.onRefresh();
           }} color="inherit" > {setting.applyButtonText} </Button>
-          <Button aria-label="dialog-cancel-button" onClick={(event) => {
+          <Button aria-label="dialog-cancel-button" onClick={() => {
             this.onCancel();
           }} color="secondary"> {setting.cancelButtonText} </Button>
         </DialogActions>
@@ -110,7 +107,7 @@ class RefreshNetworkElementsDialogComponent extends React.Component<RefreshNetwo
 
   private onCancel = () => {
     this.props.onClose();
-  }
+  };
 }
 
 export const RefreshNetworkElementsDialog = connect(undefined, mapDispatch)(RefreshNetworkElementsDialogComponent);
index 6a18252..b386dcd 100644 (file)
 
 import { IActionHandler } from '../../../../framework/src/flux/action';
 import { combineActionHandler } from '../../../../framework/src/flux/middleware';
-import { INetworkElementsState, networkElementsActionHandler } from './networkElementsHandler';
-import { IConnectionStatusLogState, connectionStatusLogActionHandler } from './connectionStatusLogHandler';
-import { IInfoNetworkElementsState, infoNetworkElementsActionHandler, IInfoNetworkElementFeaturesState, infoNetworkElementFeaturesActionHandler } from './infoNetworkElementHandler';
-import { SetPanelAction, AddWebUriList, RemoveWebUri, SetWeburiSearchBusy } from '../actions/commonNetworkElementsActions';
-import { PanelId } from '../models/panelId';
+
+import { AddWebUriList, RemoveWebUri, SetPanelAction } from '../actions/commonNetworkElementsActions';
 import { guiCutThrough } from '../models/guiCutTrough';
+import { PanelId } from '../models/panelId';
+import { connectionStatusLogActionHandler, IConnectionStatusLogState } from './connectionStatusLogHandler';
+import { IInfoNetworkElementFeaturesState, IInfoNetworkElementsState, infoNetworkElementFeaturesActionHandler, infoNetworkElementsActionHandler } from './infoNetworkElementHandler';
+import { INetworkElementsState, networkElementsActionHandler } from './networkElementsHandler';
 import { availableTlsKeysActionHandler, IAvailableTlsKeysState } from './tlsKeyHandler';
 
 export interface IConnectAppStoreState {
@@ -33,7 +34,7 @@ export interface IConnectAppStoreState {
   elementInfo: IInfoNetworkElementsState;
   elementFeatureInfo: IInfoNetworkElementFeaturesState;
   guiCutThrough: guiCutThroughState;
-  availableTlsKeys: IAvailableTlsKeysState
+  availableTlsKeys: IAvailableTlsKeysState;
 }
 
 const currentOpenPanelHandler: IActionHandler<PanelId> = (state = null, action) => {
@@ -41,7 +42,7 @@ const currentOpenPanelHandler: IActionHandler<PanelId> = (state = null, action)
     state = action.panelId;
   }
   return state;
-}
+};
 
 interface guiCutThroughState {
   searchedElements: guiCutThrough[];
@@ -62,12 +63,12 @@ const guiCutThroughHandler: IActionHandler<guiCutThroughState> = (state = { sear
     if (action.newlySearchedElements) {
       action.newlySearchedElements.forEach(item => {
         notSearchedElements = notSearchedElements.filter(id => id !== item);
-      })
+      });
     }
 
     searchedElements = state.searchedElements.concat(action.searchedElements);
 
-    state = { searchedElements: searchedElements, notSearchedElements: notSearchedElements, unsupportedElements: unsupportedElements }
+    state = { searchedElements: searchedElements, notSearchedElements: notSearchedElements, unsupportedElements: unsupportedElements };
 
   } else if (action instanceof RemoveWebUri) {
     const nodeId = action.element;
@@ -77,11 +78,11 @@ const guiCutThroughHandler: IActionHandler<guiCutThroughState> = (state = { sear
     state = { notSearchedElements: knownElements, searchedElements: webUris, unsupportedElements: unsupportedElement };
   }
   return state;
-}
+};
 
 declare module '../../../../framework/src/store/applicationStore' {
   interface IApplicationStoreState {
-    connect: IConnectAppStoreState
+    connect: IConnectAppStoreState;
   }
 }
 
@@ -92,7 +93,7 @@ const actionHandlers = {
   elementInfo: infoNetworkElementsActionHandler,
   elementFeatureInfo: infoNetworkElementFeaturesActionHandler,
   guiCutThrough: guiCutThroughHandler,
-  availableTlsKeys: availableTlsKeysActionHandler
+  availableTlsKeys: availableTlsKeysActionHandler,
 };
 
 export const connectAppRootHandler = combineActionHandler<IConnectAppStoreState>(actionHandlers);
index 6863ec3..264b6c1 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import { createExternal,IExternalTableState } from '../../../../framework/src/components/material-table/utilities';
+import { createExternal, IExternalTableState } from '../../../../framework/src/components/material-table/utilities';
 import { createSearchDataHandler } from '../../../../framework/src/utilities/elasticSearch';
 
 import { NetworkElementConnectionLog } from '../models/networkElementConnectionLog';
+
 export interface IConnectionStatusLogState extends IExternalTableState<NetworkElementConnectionLog> { }
 
 // create eleactic search material data fetch handler
index 3e2d1ce..692e63a 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
- import { IActionHandler } from '../../../../framework/src/flux/action';
+import { IActionHandler } from '../../../../framework/src/flux/action';
 
- import { AllElementInfoLoadedAction, AllElementInfoFeatureLoadedAction, LoadAllElementInfoAction } from '../actions/infoNetworkElementActions';
+import { AllElementInfoFeatureLoadedAction, AllElementInfoLoadedAction, LoadAllElementInfoAction } from '../actions/infoNetworkElementActions';
+import { Module, TopologyNode } from '../models/topologyNetconf';
  
- import { Module, TopologyNode } from '../models/topologyNetconf';
+export interface IInfoNetworkElementsState {
+  elementInfo: TopologyNode;
+  busy: boolean;
+}
  
- export interface IInfoNetworkElementsState {
-   elementInfo: TopologyNode;
-   busy: boolean;
- }
+export interface IInfoNetworkElementFeaturesState {
+  elementFeatureInfo: Module[];
+  busy: boolean;
+}
  
- export interface IInfoNetworkElementFeaturesState {
-   elementFeatureInfo: Module[];
-   busy: boolean;
- }
+const infoNetworkElementsStateInit: IInfoNetworkElementsState = {
+  elementInfo: {
+    'node-id': '',
+    'netconf-node-topology:available-capabilities': {
+      'available-capability': [],
+    },
+  },
+  busy: false,
+};
  
- const infoNetworkElementsStateInit: IInfoNetworkElementsState = {
-   elementInfo: {
-     "node-id": "",
-     "netconf-node-topology:available-capabilities": {
-       "available-capability": []
-     }
-   },
-   busy: false
- };
+const infoNetworkElementFeaturesStateInit: IInfoNetworkElementFeaturesState = {
+  elementFeatureInfo: [],
+  busy: false,
+};
  
- const infoNetworkElementFeaturesStateInit: IInfoNetworkElementFeaturesState = {
-   elementFeatureInfo: [],
-   busy: false
- };
+export const infoNetworkElementsActionHandler: IActionHandler<IInfoNetworkElementsState> = (state = infoNetworkElementsStateInit, action) => {
+  if (action instanceof LoadAllElementInfoAction) {
+    state = {
+      ...state,
+      busy: true,
+    };
+  } else if (action instanceof AllElementInfoLoadedAction) {
+    if (!action.error && action.elementInfo) {
+      state = {
+        ...state,
+        elementInfo: action.elementInfo,
+        busy: false,
+      };
+    } else {
+      state = {
+        ...state,
+        busy: false,
+      };
+    }
+  }
+  return state;
+};
  
- export const infoNetworkElementsActionHandler: IActionHandler<IInfoNetworkElementsState> = (state = infoNetworkElementsStateInit, action) => {
-   if (action instanceof LoadAllElementInfoAction) {
-     state = {
-       ...state,
-       busy: true
-     };
-   } else if (action instanceof AllElementInfoLoadedAction) {
-     if (!action.error && action.elementInfo) {
-       state = {
-         ...state,
-         elementInfo: action.elementInfo,
-         busy: false
-       };
-     } else {
-       state = {
-         ...state,
-         busy: false
-       };
-     }
-   }
-   return state;
- };
- export const infoNetworkElementFeaturesActionHandler: IActionHandler<IInfoNetworkElementFeaturesState> = (state = infoNetworkElementFeaturesStateInit, action) => {
-   if (action instanceof LoadAllElementInfoAction) {
-     state = {
-       ...state,
-       busy: true
-     };
-   } else if (action instanceof AllElementInfoFeatureLoadedAction) {
-     if (!action.error && action.elementFeatureInfo) {
-       state = {
-         ...state,
-         elementFeatureInfo: action.elementFeatureInfo,
-         busy: false
-       };
-     } else {
-       state = {
-         ...state,
-         busy: false
-       };
-     }
-   }
-   return state;
- };
\ No newline at end of file
+export const infoNetworkElementFeaturesActionHandler: IActionHandler<IInfoNetworkElementFeaturesState> = (state = infoNetworkElementFeaturesStateInit, action) => {
+  if (action instanceof LoadAllElementInfoAction) {
+    state = {
+      ...state,
+      busy: true,
+    };
+  } else if (action instanceof AllElementInfoFeatureLoadedAction) {
+    if (!action.error && action.elementFeatureInfo) {
+      state = {
+        ...state,
+        elementFeatureInfo: action.elementFeatureInfo,
+        busy: false,
+      };
+    } else {
+      state = {
+        ...state,
+        busy: false,
+      };
+    }
+  }
+  return state;
+};
\ No newline at end of file
index b74a394..42d2824 100644 (file)
@@ -16,8 +16,8 @@
  * ============LICENSE_END==========================================================================
  */
 import { createExternal, IExternalTableState } from '../../../../framework/src/components/material-table/utilities';
-import { createSearchDataHandler } from '../../../../framework/src/utilities/elasticSearch';
 import { getAccessPolicyByUrl } from '../../../../framework/src/services/restService';
+import { createSearchDataHandler } from '../../../../framework/src/utilities/elasticSearch';
 
 import { NetworkElementConnection } from '../models/networkElementConnection';
 import { connectService } from '../services/connectService';
@@ -32,7 +32,7 @@ export const {
   createActions: createNetworkElementsActions,
   createProperties: createNetworkElementsProperties,
   reloadAction: networkElementsReloadAction,
-  
+
   // set value action, to change a value
 } = createExternal<NetworkElementConnection>(networkElementsSearchHandler, appState => {
 
@@ -42,20 +42,19 @@ export const {
 
     appState.connect.networkElements.rows.forEach(element => {
 
-      if (element.status === "Connected") {
+      if (element.status === 'Connected') {
         const webUri = webUris.find(item => item.id === element.id as string);
         if (webUri) {
           element.weburi = webUri.weburi;
           element.isWebUriUnreachable = false;
-        }
-        else {
-          element.isWebUriUnreachable = true
+        } else {
+          element.isWebUriUnreachable = true;
         }
       }
     });
   }
 
-  return appState.connect.networkElements
+  return appState.connect.networkElements;
 }, (ne) => {
   if (!ne || !ne.id) return true;
   const neUrl = connectService.getNetworkElementUri(ne.id);
index 326b3cc..20badcb 100644 (file)
@@ -34,7 +34,7 @@ export const availableTlsKeysActionHandler: IActionHandler<IAvailableTlsKeysStat
   if (action instanceof LoadAllTlsKeyListAction) {
     state = {
       ...state,
-      busy: true
+      busy: true,
     };
 
   } else if (action instanceof AllTlsKeyListLoadedAction) {
@@ -47,7 +47,7 @@ export const availableTlsKeysActionHandler: IActionHandler<IAvailableTlsKeysStat
     } else {
       state = {
         ...state,
-        busy: false
+        busy: false,
       };
     }
   }
index b9f515d..0fd46a8 100644 (file)
@@ -17,6 +17,6 @@
  */
 
 export type guiCutThrough = {
-    id: string,
-    weburi?: string
-}
\ No newline at end of file
+  id: string;
+  weburi?: string;
+};
\ No newline at end of file
index 39278a8..a46a30e 100644 (file)
@@ -16,7 +16,7 @@
  * ============LICENSE_END==========================================================================
  */
 export type NetworkElementBaseType = {
-  mountId: string,
-  host: string,
-  port: number,
-}
\ No newline at end of file
+  mountId: string;
+  host: string;
+  port: number;
+};
\ No newline at end of file
index bc15afb..bb076c7 100644 (file)
@@ -27,7 +27,7 @@ export type NetworkElementConnection = {
   tlsKey?: string;
   weburi?: string;
   isWebUriUnreachable?: boolean;
-  status?: "Connected" | "mounted" | "unmounted" | "Connecting" | "Disconnected" | "idle";
+  status?: 'Connected' | 'mounted' | 'unmounted' | 'Connecting' | 'Disconnected' | 'idle';
   coreModelCapability?: string;
   deviceType?: string;
   deviceFunction?: string;
@@ -40,8 +40,8 @@ export type NetworkElementConnection = {
       failureReason: string;
       capability: string;
     }[];
-  }
-}
+  };
+};
 
 
 export type UpdateNetworkElement = {
@@ -50,15 +50,15 @@ export type UpdateNetworkElement = {
   username?: string;
   password?: string;
   tlsKey?: string;
-}
+};
 
 export type ConnectionStatus = {
-  status: string
-}
+  status: string;
+};
 
 export type TlsKeys = {
-  key: string
-}
+  key: string;
+};
 
 
 /**
index a1535cb..4b4e283 100644 (file)
@@ -19,7 +19,7 @@
 export type NetworkElementConnectionLog = {
   id: string;
   nodeId: string;
-  status: "connected" | "mounted" | "unmounted" | "connecting" | "disconnected" | "idle";
+  status: 'connected' | 'mounted' | 'unmounted' | 'connecting' | 'disconnected' | 'idle';
   timestamp: string;
-}
+};
 
index 2000d94..2861f10 100644 (file)
@@ -16,4 +16,4 @@
  * ============LICENSE_END==========================================================================
  */
 
-export type PanelId = null | "NetworkElements" | "ConnectionStatusLog";
\ No newline at end of file
+export type PanelId = null | 'NetworkElements' | 'ConnectionStatusLog';
\ No newline at end of file
index 936e20b..85a1a71 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
- export interface AvailableCapability {
-  "capability-origin": string;
+export interface AvailableCapability {
+  'capability-origin': string;
   capability: string;
 }
 
 export interface NetconfNodeTopologyAvailableCapabilities {
-  "available-capability": AvailableCapability[];
+  'available-capability': AvailableCapability[];
 }
 
 export interface TopologyNode {
-  "node-id": string;
-  "netconf-node-topology:available-capabilities": NetconfNodeTopologyAvailableCapabilities;
+  'node-id': string;
+  'netconf-node-topology:available-capabilities': NetconfNodeTopologyAvailableCapabilities;
 }
 
 export interface Topology {
-  "topology-id": string;
-  "network-topology:node": TopologyNode[];
+  'topology-id': string;
+  'network-topology:node': TopologyNode[];
 }
 
 /**
@@ -47,13 +47,13 @@ export interface Module {
 }
 
 export interface ModuleFeatures {
-   module: Module[];
+  module: Module[];
 }
 
 export interface ModuleSet {
-  "module-set": ModuleFeatures[];
+  'module-set': ModuleFeatures[];
 }
 
 export interface FeatureTopology {
-  "ietf-yang-library:yang-library" : ModuleSet 
+  'ietf-yang-library:yang-library' : ModuleSet; 
 }
index c8cf704..b69993f 100644 (file)
@@ -16,9 +16,9 @@
  * ============LICENSE_END==========================================================================
  */
 
- export type AvailableCapabilities = {
-    id?: string,
-    module: string,
-    revision: string,
-    features: string
-}
\ No newline at end of file
+export type AvailableCapabilities = {
+  id?: string;
+  module: string;
+  revision: string;
+  features: string;
+};
\ No newline at end of file
index 2a9a46d..c290716 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 React from 'react';
+import { Redirect, Route, RouteComponentProps, Switch, withRouter } from 'react-router-dom';
 
-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 { connect, Connect, IDispatcher } from '../../../framework/src/flux/connect';
+import applicationManager from '../../../framework/src/services/applicationManager';
+import { IFormatedMessage, subscribe } from '../../../framework/src/services/notificationService';
+import { IApplicationStoreState } from '../../../framework/src/store/applicationStore';
 
-import { findWebUrisForGuiCutThroughAsyncAction, updateCurrentViewAsyncAction, SetPanelAction } from './actions/commonNetworkElementsActions';
-import { createNetworkElementsActions, createNetworkElementsProperties, networkElementsReloadAction } from './handlers/networkElementsHandler';
+import { findWebUrisForGuiCutThroughAsyncAction, SetPanelAction, updateCurrentViewAsyncAction } from './actions/commonNetworkElementsActions';
+import { NetworkElementsList } from './components/networkElements';
 import connectAppRootHandler from './handlers/connectAppRootHandler';
+import { createNetworkElementsActions, createNetworkElementsProperties, networkElementsReloadAction } from './handlers/networkElementsHandler';
+import { PanelId } from './models/panelId';
 import ConnectApplication from './views/connectView';
-import { PanelId } from "./models/panelId";
-import { NetworkElementsList } from './components/networkElements';
 
+const appIcon = require('./assets/icons/connectAppIcon.svg');  // select app icon
 
 let currentStatus: string | undefined = undefined;
-let refreshInterval: ReturnType<typeof window.setInterval> | null = null;
-
 
 const mapProps = (state: IApplicationStoreState) => ({
   networkElementDashboardProperties: createNetworkElementsProperties(state),
@@ -48,24 +46,25 @@ const mapDisp = (dispatcher: IDispatcher) => ({
 });
 
 const ConnectApplicationRouteAdapter = connect(mapProps, mapDisp)((props: RouteComponentProps<{ status?: string }> & Connect<typeof mapProps, typeof mapDisp>) => {
+  
+  // TODO: move into useEffect!
   if (currentStatus !== props.match.params.status) {
     currentStatus = props.match.params.status || undefined;
     window.setTimeout(() => {
       if (currentStatus) {
-        props.setCurrentPanel("NetworkElements");
-        props.networkElementsDashboardActions.onFilterChanged("status", currentStatus);
+        props.setCurrentPanel('NetworkElements');
+        props.networkElementsDashboardActions.onFilterChanged('status', currentStatus);
         if (!props.networkElementDashboardProperties.showFilter) {
           props.networkElementsDashboardActions.onToggleFilter(false);
           props.networkElementsDashboardActions.onRefresh();
-        }
-        else
+        } else
           props.networkElementsDashboardActions.onRefresh();
       }
     });
   }
   return (
     <NetworkElementsList />
-  )
+  );
 });
 
 
@@ -79,19 +78,19 @@ const App = withRouter((props: RouteComponentProps) => (
 
 export function register() {
   const applicationApi = applicationManager.registerApplication({
-    name: "connect",
-    icon: faPlug,
+    name: 'connect',
+    icon: appIcon,
     rootComponent: App,
     rootActionHandler: connectAppRootHandler,
-    menuEntry: "Connect"
+    menuEntry: 'Connect',
   });
 
   // subscribe to the websocket notifications
-  subscribe<IFormatedMessage>(["object-creation-notification", "object-deletion-notification", "attribute-value-changed-notification"], (msg => {
+  subscribe<IFormatedMessage>(['object-creation-notification', 'object-deletion-notification', 'attribute-value-changed-notification'], (msg => {
     const store = applicationApi.applicationStore;
-    if (msg && msg.type.type === "object-creation-notification" && store) {
+    if (msg && msg.type.type === 'object-creation-notification' && store) {
       store.dispatch(new AddSnackbarNotification({ message: `Adding node [${msg.data['object-id-ref']}]`, options: { variant: 'info' } }));
-    } else if (msg && (msg.type.type === "object-deletion-notification" || msg.type.type === "attribute-value-changed-notification") && store) {
+    } else if (msg && (msg.type.type === 'object-deletion-notification' || msg.type.type === 'attribute-value-changed-notification') && store) {
       store.dispatch(new AddSnackbarNotification({ message: `Updating node [${msg.data['object-id-ref']}]`, options: { variant: 'info' } }));
     }
     if (store) {
index 08cc580..1d74f85 100644 (file)
@@ -18,7 +18,7 @@
 
 import { requestRest } from '../../../../framework/src/services/restService';
 import { NetworkElementConnection, ConnectionStatus, UpdateNetworkElement } from '../models/networkElementConnection';
-import { TlsKeys } from '../models/networkElementConnection'
+import { TlsKeys } from '../models/networkElementConnection';
 import { convertPropertyNames, replaceUpperCase } from '../../../../framework/src/utilities/yangHelper';
 import { Result } from '../../../../framework/src/models/elasticSearch';
 
@@ -30,17 +30,20 @@ import { guiCutThrough } from '../models/guiCutTrough';
 */
 class ConnectService {
   public getNetworkElementUri = (nodeId: string) => '/rests/data/network-topology:network-topology/topology=topology-netconf/node=' + nodeId;
-  public getNetworkElementConnectDataProviderUri = (operation: "create" | "update" | "delete") => `/rests/operations/data-provider:${operation}-network-element-connection`;
+
+  public getNetworkElementConnectDataProviderUri = (operation: 'create' | 'update' | 'delete') => `/rests/operations/data-provider:${operation}-network-element-connection`;
+
   public getAllWebUriExtensionsForNetworkElementListUri = (nodeId: string) => this.getNetworkElementUri(nodeId) + '/yang-ext:mount/core-model:network-element';
-  public getNetworkElementYangLibraryFeature = (nodeId: string) => '/rests/data/network-topology:network-topology/topology=topology-netconf/node=' + nodeId + '/yang-ext:mount/ietf-yang-library:yang-library?content=nonconfig'
+
+  public getNetworkElementYangLibraryFeature = (nodeId: string) => '/rests/data/network-topology:network-topology/topology=topology-netconf/node=' + nodeId + '/yang-ext:mount/ietf-yang-library:yang-library?content=nonconfig';
 
   /**
    * Inserts a network element/node.
    */
   public async createNetworkElement(element: NetworkElementConnection): Promise<NetworkElementConnection | null> {
-    const path = this.getNetworkElementConnectDataProviderUri("create");
+    const path = this.getNetworkElementConnectDataProviderUri('create');
     const result = await requestRest<NetworkElementConnection>(path, {
-      method: "POST", body: JSON.stringify(convertPropertyNames({ "data-provider:input": element }, replaceUpperCase))
+      method: 'POST', body: JSON.stringify(convertPropertyNames({ 'data-provider:input': element }, replaceUpperCase)),
     });
     return result || null;
   }
@@ -49,9 +52,9 @@ class ConnectService {
   * Updates a network element/node.
   */
   public async updateNetworkElement(element: UpdateNetworkElement): Promise<NetworkElementConnection | null> {
-    const path = this.getNetworkElementConnectDataProviderUri("update");
+    const path = this.getNetworkElementConnectDataProviderUri('update');
     const result = await requestRest<NetworkElementConnection>(path, {
-      method: "POST", body: JSON.stringify(convertPropertyNames({ "data-provider:input": element }, replaceUpperCase))
+      method: 'POST', body: JSON.stringify(convertPropertyNames({ 'data-provider:input': element }, replaceUpperCase)),
     });
     return result || null;
   }
@@ -61,11 +64,11 @@ class ConnectService {
     */
   public async deleteNetworkElement(element: UpdateNetworkElement): Promise<NetworkElementConnection | null> {
     const query = {
-      "id": element.id
+      'id': element.id,
     };
-    const path = this.getNetworkElementConnectDataProviderUri("delete");
+    const path = this.getNetworkElementConnectDataProviderUri('delete');
     const result = await requestRest<NetworkElementConnection>(path, {
-      method: "POST", body: JSON.stringify(convertPropertyNames({ "data-provider:input": query }, replaceUpperCase))
+      method: 'POST', body: JSON.stringify(convertPropertyNames({ 'data-provider:input': query }, replaceUpperCase)),
     });
     return result || null;
   }
@@ -107,13 +110,12 @@ class ConnectService {
       '<name xmlns="urn:opendaylight:netconf-node-topology">TLS</name>',
       ' </protocol>',
       '<max-connection-attempts xmlns="urn:opendaylight:netconf-node-topology">2</max-connection-attempts>',
-      '</node>'].join('')
+      '</node>'].join('');
     let bodyXml;
     if (networkElement.password) {
-      bodyXml = mountXml
-    }
-    else {
-      bodyXml = tlsXml
+      bodyXml = mountXml;
+    } else {
+      bodyXml = tlsXml;
     }
 
     try {
@@ -121,16 +123,16 @@ class ConnectService {
         method: 'PUT',
         headers: {
           'Content-Type': 'application/xml',
-          'Accept': 'application/xml'
+          'Accept': 'application/xml',
         },
-        body: bodyXml
+        body: bodyXml,
       });
       // expect an empty answer
       return result !== null;
     } catch {
       return false;
     }
-  };
+  }
 
   /** Unmounts a network element by its id. */
   public async unmountNetworkElement(nodeId: string): Promise<boolean> {
@@ -141,7 +143,7 @@ class ConnectService {
         method: 'DELETE',
         headers: {
           'Content-Type': 'application/xml',
-          'Accept': 'application/xml'
+          'Accept': 'application/xml',
         },
       });
       // expect an empty answer
@@ -150,15 +152,15 @@ class ConnectService {
     } catch {
       return false;
     }
-  };
+  }
 
   /** Yang capabilities of the selected network element/node */
   public async infoNetworkElement(nodeId: string): Promise<TopologyNode | null> {
     const path = this.getNetworkElementUri(nodeId);
-    const topologyRequestPomise = requestRest<Topology>(path, { method: "GET" });
+    const topologyRequestPomise = requestRest<Topology>(path, { method: 'GET' });
 
     return topologyRequestPomise && topologyRequestPomise.then(result => {
-      return result && result["network-topology:node"] && result["network-topology:node"][0] || null;
+      return result && result['network-topology:node'] && result['network-topology:node'][0] || null;
     });
   }
 
@@ -166,13 +168,13 @@ class ConnectService {
   /** Yang features of the selected network element/node module */
   public async infoNetworkElementFeatures(nodeId: string): Promise<Module[] | null | undefined> {
     const path = this.getNetworkElementYangLibraryFeature(nodeId);
-    const topologyRequestPomise = requestRest<FeatureTopology>(path, { method: "GET" });
+    const topologyRequestPomise = requestRest<FeatureTopology>(path, { method: 'GET' });
 
     return topologyRequestPomise && topologyRequestPomise.then(result => {
       const resultFinal = result && result['ietf-yang-library:yang-library']
-        && result["ietf-yang-library:yang-library"]["module-set"] &&
-        result["ietf-yang-library:yang-library"]["module-set"][0] &&
-        result["ietf-yang-library:yang-library"]["module-set"][0]['module'] || null;
+        && result['ietf-yang-library:yang-library']['module-set'] &&
+        result['ietf-yang-library:yang-library']['module-set'][0] &&
+        result['ietf-yang-library:yang-library']['module-set'][0].module || null;
       return resultFinal;
     });
   }
@@ -183,22 +185,22 @@ class ConnectService {
    * Get the connection state of the network element/ node
    */
   public async getNetworkElementConnectionStatus(element: string): Promise<(ConnectionStatus)[] | null> {
-    const path = `/rests/operations/data-provider:read-network-element-connection-list`;
+    const path = '/rests/operations/data-provider:read-network-element-connection-list';
     const query = {
-      "data-provider:input": {
-        "filter": [{
-          "property": "node-id",
-          "filtervalue": element
+      'data-provider:input': {
+        'filter': [{
+          'property': 'node-id',
+          'filtervalue': element,
         }],
-        "pagination": {
-          "size": 20,
-          "page": 1
-        }
-      }
-    }
-    const result = await requestRest<Result<ConnectionStatus>>(path, { method: "POST", body: JSON.stringify(query) });
-    return result && result["data-provider:output"] && result["data-provider:output"].data && result["data-provider:output"].data.map(ne => ({
-      status: ne.status
+        'pagination': {
+          'size': 20,
+          'page': 1,
+        },
+      },
+    };
+    const result = await requestRest<Result<ConnectionStatus>>(path, { method: 'POST', body: JSON.stringify(query) });
+    return result && result['data-provider:output'] && result['data-provider:output'].data && result['data-provider:output'].data.map(ne => ({
+      status: ne.status,
     })) || null;
   }
 
@@ -209,44 +211,43 @@ class ConnectService {
   public async getTlsKeys(): Promise<(TlsKeys)[] | null> {
     const path = '/rests/operations/data-provider:read-tls-key-entry';
     const query = {
-      "data-provider:input": {
-        "filter": [],
-        "sortorder": [],
-        "pagination": {
-          "size": 20,
-          "page": 1
-        }
-      }
+      'data-provider:input': {
+        'filter': [],
+        'sortorder': [],
+        'pagination': {
+          'size': 20,
+          'page': 1,
+        },
+      },
     };
 
-    const result = await requestRest<Result<string>>(path, { method: "POST", body: JSON.stringify(query) });
-    return result && result["data-provider:output"] && result["data-provider:output"].data && result["data-provider:output"].data.map(ne => ({
-      key: ne
+    const result = await requestRest<Result<string>>(path, { method: 'POST', body: JSON.stringify(query) });
+    return result && result['data-provider:output'] && result['data-provider:output'].data && result['data-provider:output'].data.map(ne => ({
+      key: ne,
     })) || null;
   }
 
   public async getAllWebUriExtensionsForNetworkElementListAsync(neList: string[]): Promise<(guiCutThrough)[]> {
-    const path = `/rests/operations/data-provider:read-gui-cut-through-entry`;
-    let webUriList: guiCutThrough[] = []
+    const path = '/rests/operations/data-provider:read-gui-cut-through-entry';
+    let webUriList: guiCutThrough[] = [];
     const query = {
-      "data-provider:input": {
-        "filter": [{
-          "property": "id",
-          "filtervalues": neList
+      'data-provider:input': {
+        'filter': [{
+          'property': 'id',
+          'filtervalues': neList,
         }],
-        "pagination": {
-          "size": 20,
-          "page": 1
-        }
-      }
-    }
+        'pagination': {
+          'size': 20,
+          'page': 1,
+        },
+      },
+    };
 
-    const result = await requestRest<Result<guiCutThrough>>(path, { method: "POST", body: JSON.stringify(query) });
-    const resultData = result && result["data-provider:output"] && result["data-provider:output"].data;
+    const result = await requestRest<Result<guiCutThrough>>(path, { method: 'POST', body: JSON.stringify(query) });
+    const resultData = result && result['data-provider:output'] && result['data-provider:output'].data;
     neList.forEach(nodeId => {
       let entryNotFound = true;
       if (resultData) {
-        const BreakException = {};
         try {
           resultData.forEach(entry => {
             if (entry.id == nodeId) {
@@ -256,7 +257,7 @@ class ConnectService {
               } else {
                 webUriList.push({ id: nodeId, weburi: undefined });
               }
-              throw BreakException;
+              throw new Error();
             }
           });
         } catch (e) { }
index 0828397..a6fcb7c 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
+import React from 'react';
 
-import connect, { IDispatcher, Connect } from '../../../../framework/src/flux/connect';
-import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
-import { Panel } from '../../../../framework/src/components/material-ui';
-import { networkElementsReloadAction, createNetworkElementsActions } from '../handlers/networkElementsHandler';
-import { connectionStatusLogReloadAction, createConnectionStatusLogActions } from '../handlers/connectionStatusLogHandler';
+import { AppBar, Tab, Tabs } from '@mui/material';
 
-import { NetworkElementsList } from '../components/networkElements';
+import { useApplicationDispatch, useSelectApplicationState } from '../../../../framework/src/flux/connect';
+
+import { findWebUrisForGuiCutThroughAsyncAction, setPanelAction } from '../actions/commonNetworkElementsActions';
 import { ConnectionStatusLog } from '../components/connectionStatusLog';
-import { setPanelAction, findWebUrisForGuiCutThroughAsyncAction, SetWeburiSearchBusy } from '../actions/commonNetworkElementsActions';
+import { NetworkElementsList } from '../components/networkElements';
+import { connectionStatusLogReloadAction } from '../handlers/connectionStatusLogHandler';
+import { networkElementsReloadAction } from '../handlers/networkElementsHandler';
+import { NetworkElementConnection } from '../models/networkElementConnection';
 import { PanelId } from '../models/panelId';
-import { NetworkElementConnection } from 'models/networkElementConnection';
-import { AppBar, Tabs, Tab } from '@mui/material';
-
-const mapProps = (state: IApplicationStoreState) => ({
-  panelId: state.connect.currentOpenPanel,
-  user: state.framework.authenticationState.user,
-  netWorkElements: state.connect.networkElements,
-  availableGuiCutroughs: state.connect.guiCutThrough
-});
-
-const mapDispatcher = (dispatcher: IDispatcher) => ({
-  networkElementsActions: createNetworkElementsActions(dispatcher.dispatch),
-  connectionStatusLogActions: createConnectionStatusLogActions(dispatcher.dispatch),
-  onLoadNetworkElements: () => dispatcher.dispatch(networkElementsReloadAction),
-  loadWebUris: (networkElements: NetworkElementConnection[]) => 
-    dispatcher.dispatch(findWebUrisForGuiCutThroughAsyncAction(networkElements.map((ne) => ne.id!))),
-  isBusy: (busy: boolean) => dispatcher.dispatch(new SetWeburiSearchBusy(busy)),
-  onLoadConnectionStatusLog: () => {
-    dispatcher.dispatch(connectionStatusLogReloadAction);
-  },
-  switchActivePanel: (panelId: PanelId) => {
-    dispatcher.dispatch(setPanelAction(panelId));
-  }
-});
 
-type ConnectApplicationComponentProps = Connect<typeof mapProps, typeof mapDispatcher>;
+const ConnectApplicationComponent:  React.FC<{}> = () => {
 
-class ConnectApplicationComponent extends React.Component<ConnectApplicationComponentProps>{
+  const panelId = useSelectApplicationState(state => state.connect.currentOpenPanel);
+  const netWorkElements = useSelectApplicationState(state => state.connect.networkElements);
 
-  public componentDidMount() {
-    if (this.props.panelId === null) { //don't change tabs, if one is selected already
-      this.onTogglePanel("NetworkElements");
-    }
-    //this.props.networkElementsActions.onToggleFilter();
-    //this.props.connectionStatusLogActions.onToggleFilter();
-  }
-
-  public componentDidUpdate = () => {
-    
-    const networkElements = this.props.netWorkElements;
-
-    if (networkElements.rows.length > 0) {
-      // Update all netWorkElements for propper WebUriClient settings in case of table data changes.
-      // e.G: Pagination of the table data (there is no event)
-      this.props.loadWebUris(networkElements.rows);
-    }
-  }
+  const dispatch = useApplicationDispatch();
+  const onLoadNetworkElements = () => dispatch(networkElementsReloadAction);
+  const loadWebUris = (networkElements: NetworkElementConnection[]) => dispatch(findWebUrisForGuiCutThroughAsyncAction(networkElements.map((ne) => ne.id!)));
+  const onLoadConnectionStatusLog = () => dispatch(connectionStatusLogReloadAction);
+  const switchActivePanel = (panelId2: PanelId) => dispatch(setPanelAction(panelId2));
 
-  private onTogglePanel = (panelId: PanelId) => {
-    const nextActivePanel = panelId;
-    this.props.switchActivePanel(nextActivePanel);
+  const onTogglePanel = (panelId2: PanelId) => {
+    const nextActivePanel = panelId2;
+    switchActivePanel(nextActivePanel);
 
     switch (nextActivePanel) {
       case 'NetworkElements':
-        this.props.onLoadNetworkElements();
+        onLoadNetworkElements();
         break;
       case 'ConnectionStatusLog':
-        this.props.onLoadConnectionStatusLog();
+        onLoadConnectionStatusLog();
         break;
       case null:
         // do nothing if all panels are closed
         break;
       default:
-        console.warn("Unknown nextActivePanel [" + nextActivePanel + "] in connectView");
+        console.warn('Unknown nextActivePanel [' + nextActivePanel + '] in connectView');
         break;
     }
-
   };
 
-  private onHandleTabChange = (event: React.SyntheticEvent, newValue: PanelId) => {
-    this.props.switchActivePanel(newValue);
-  }
-
-  render(): JSX.Element {
-    const { panelId: activePanelId } = this.props;
-
-    return (
-      <>
-        <AppBar enableColorOnDark position="static">
-          <Tabs indicatorColor="secondary" textColor="inherit" value={activePanelId} onChange={this.onHandleTabChange} aria-label="connect-app-tabs">
-            <Tab aria-label="network-elements-list-tab" label="NODES" value="NetworkElements" />
-            <Tab aria-label="connection-status-log-tab" label="Connection Status Log" value="ConnectionStatusLog" />
-          </Tabs>
-        </AppBar>
-        {activePanelId === 'NetworkElements'
-          ? <NetworkElementsList />
-          : activePanelId === 'ConnectionStatusLog'
-            ? <ConnectionStatusLog />
-            : null}
-      </>
-    );
+  const onHandleTabChange = (event: React.SyntheticEvent, newValue: PanelId) => {
+    switchActivePanel(newValue);
   };
 
+  React.useEffect(()=>{
+    if (panelId === null) { //don't change tabs, if one is selected already
+      onTogglePanel('NetworkElements');
+    }
+  }, []);
 
-}
+  React.useEffect(()=>{
+    const networkElements = netWorkElements;
 
-export const ConnectApplication = (connect(mapProps, mapDispatcher)(ConnectApplicationComponent));
+    if (networkElements.rows.length > 0) {
+      // Search for weburi client for all netWorkElements in case of table data changes.
+      // e.G: Pagination of the table data (there is no event)
+      loadWebUris(networkElements.rows);
+    }
+  }, [netWorkElements]);
+
+  return (
+    <>
+      <AppBar enableColorOnDark position="static">
+        <Tabs indicatorColor="secondary" textColor="inherit" value={panelId} onChange={onHandleTabChange} aria-label="connect-app-tabs">
+          <Tab aria-label="network-elements-list-tab" label="NODES" value="NetworkElements" />
+          <Tab aria-label="connection-status-log-tab" label="Connection Status Log" value="ConnectionStatusLog" />
+        </Tabs>
+      </AppBar>
+      {panelId === 'NetworkElements'
+        ? <NetworkElementsList />
+        : panelId === 'ConnectionStatusLog'
+          ? <ConnectionStatusLog />
+          : null
+      }
+    </>
+  );
+};
+
+export const ConnectApplication = ConnectApplicationComponent;
 export default ConnectApplication;
\ No newline at end of file
index a66b5d8..ca65092 100644 (file)
@@ -4,7 +4,7 @@
     "outDir": "./dist",
     "sourceMap": true,
     "forceConsistentCasingInFileNames": true,
-    "allowSyntheticDefaultImports": false,
+    "allowSyntheticDefaultImports": true,
     "allowUnreachableCode": false,
     "allowUnusedLabels": false,
     "noFallthroughCasesInSwitch": true,
index ff76904..b7aebb9 100644 (file)
@@ -59,6 +59,16 @@ module.exports = (env) => {
         use: [{
           loader: "babel-loader"
         }]
+      },{
+        //don't minify images
+        test: /\.(png|gif|jpg|svg)$/,
+        use: [{
+          loader: 'url-loader',
+          options: {
+            limit: 10,
+            name: './images/[name].[ext]'
+          }
+        }]
       }]
     },
 
index 951332a..6a31bc3 100644 (file)
@@ -21,6 +21,9 @@
   "author": "Matthias Fischer",
   "license": "Apache-2.0",
   "dependencies": {
+    "@fortawesome/fontawesome-svg-core": "1.2.35",
+    "@fortawesome/free-solid-svg-icons": "5.6.3",
+    "@fortawesome/react-fontawesome": "0.1.14",
     "@emotion/react": "^11.7.0",
     "@emotion/styled": "^11.6.0",
     "@mui/icons-material": "^5.2.0",
index 07f990b..172a435 100644 (file)
@@ -19,6 +19,7 @@
   ~ ============LICENSE_END=======================================================
   ~
   -->
+
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
                         <!-- optional: default phase is "generate-resources" -->
                         <phase>initialize</phase>
                         <configuration>
-                            <nodeVersion>v12.13.0</nodeVersion>
+                            <nodeVersion>v12.22.0</nodeVersion>
                             <yarnVersion>v1.22.10</yarnVersion>
                         </configuration>
                     </execution>
index f750751..f22d1e0 100644 (file)
@@ -26,16 +26,13 @@ export class ApplicationBaseAction extends Action { }
 
 
 export class LoadAllAuthorsAction extends ApplicationBaseAction {
-  constructor() {
-    super();
-  }
+  
 }
 
 // in React Action is most times a Message
 export class AllAuthorsLoadedAction extends ApplicationBaseAction {
   constructor(public authors: IAuthor[] | null, public error?: string) {
     super();
-
   }
 }
 
@@ -47,5 +44,5 @@ export const loadAllAuthorsAsync = (dispatch: Dispatch) => {
     dispatch(new AllAuthorsLoadedAction(null, error));
     dispatch(new AddErrorInfoAction(error));
   });
-} 
+}; 
 
index 6b960cd..1aad974 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
+import React, { FC, useState } from 'react';
 
-export class Counter extends React.Component<{}, { counter: number }> {
-  constructor(props: {}) {
-    super(props);
+const Counter: FC = () => {
+  const [counter, setCounter] = useState(0);
+  return (
+    <button onClick={() => setCounter(counter + 1 )} color="inherit">{counter}</button>
+  );
+};
 
-    this.state = {
-      counter: 0
-    };
-  }
-  
-  render() {
-    return (
-      <button onClick={ () => this.setState({ counter: this.state.counter + 1 }) } color="inherit">{ this.state.counter }</button>
-    )
-  }
-}
\ No newline at end of file
+Counter.displayName = 'Counter';
+
+export { Counter };
\ No newline at end of file
index 9ff8450..1f920f2 100644 (file)
@@ -18,6 +18,7 @@
 
 import { combineActionHandler } from '../../../../framework/src/flux/middleware';
 
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
 import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
 
 import { listAuthorsHandler, IListAuthors } from './listAuthorsHandler';
@@ -30,7 +31,7 @@ export interface IDemoAppStoreState {
 
 declare module '../../../../framework/src/store/applicationStore' {
   interface IApplicationStoreState {
-    demo: IDemoAppStoreState
+    demo: IDemoAppStoreState;
   }
 }
 
index 34b533c..1d37a36 100644 (file)
@@ -25,9 +25,9 @@ export interface IEditAuthor {
 
 const editAuthorInit: IEditAuthor = {
   author: null,
-  isDirty: false
+  isDirty: false,
 };
 
-export const editAuthorHandler: IActionHandler<IEditAuthor> = (state = editAuthorInit, action) => {
+export const editAuthorHandler: IActionHandler<IEditAuthor> = (state = editAuthorInit, _action) => {
   return state;
 };
index ca2b6d3..c85eaff 100644 (file)
@@ -27,7 +27,7 @@ export interface IListAuthors {
 
 const listAuthorsInit: IListAuthors = {
   authors: [],
-  busy: false
+  busy: false,
 };
 
 export const listAuthorsHandler: IActionHandler<IListAuthors> = (state = listAuthorsInit, action) => {
@@ -35,7 +35,7 @@ export const listAuthorsHandler: IActionHandler<IListAuthors> = (state = listAut
 
     state = {
       ...state,
-      busy: true
+      busy: true,
     };
   
   } else if (action instanceof AllAuthorsLoadedAction) {
@@ -43,12 +43,12 @@ export const listAuthorsHandler: IActionHandler<IListAuthors> = (state = listAut
       state = {
         ...state,
         authors: action.authors,
-        busy: false
+        busy: false,
       };
     } else {
       state = {
         ...state,
-        busy: false
+        busy: false,
       };
     }
   }
index 0aaa308..bdd414c 100644 (file)
@@ -21,7 +21,7 @@
  */
 export interface IAuthor {
   /**
-   * Defines the unique id of the autor. 
+   * Defines the unique id of the author. 
    */
   id: number;
 
index 4d67c28..7b29b40 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from "react";
+import React from 'react';
 import { withRouter, RouteComponentProps, Route, Switch, Redirect } from 'react-router-dom';
 
-import { faAddressBook, faRegistered } from '@fortawesome/free-solid-svg-icons';
+import { faAddressBook } from '@fortawesome/free-solid-svg-icons/faAddressBook';
 
 import applicationManager from '../../../framework/src/services/applicationManager';
-import connect, { Connect } from '../../../framework/src/flux/connect';
+import { connect, Connect } from '../../../framework/src/flux/connect';
 
 import { demoAppRootHandler } from './handlers/demoAppRootHandler';
 
@@ -43,12 +43,12 @@ const App = (props: AppProps) => (
 const FinalApp = withRouter(connect()(App));
 
 export function register() {
-  const applicationApi = applicationManager.registerApplication({
-    name: "demo",
+  applicationManager.registerApplication({
+    name: 'demo',
     icon: faAddressBook,
     rootComponent: FinalApp,
     rootActionHandler: demoAppRootHandler,
     exportedComponents: { counter: Counter },
-    menuEntry: "Demo"
+    menuEntry: 'Demo',
   });
 }
index 13e4b31..deaa2ff 100644 (file)
@@ -26,39 +26,39 @@ const base_url = 'https://api.mfico.de/v1/authors';
  */
 class AuthorService {
 
- /**
 /**
    * Gets all known authors from the backend.
    * @returns A promise of the type array of @see {@link IAuthor} containing all known authors.
    */
   public getAllAuthors(): Promise<IAuthor[]> {
     return new Promise((resolve: (value: IAuthor[]) => void, reject: (err: any) => void) => {
-      $.ajax({ method: "GET", url: base_url })
-        .then((data) => { resolve(data); }, (err) => { reject(err) });
+      $.ajax({ method: 'GET', url: base_url })
+        .then((data) => { resolve(data); }, (err) => { reject(err); });
     });
   }
 
- /**
 /**
    * Gets an author by its id from the backend.
    * @returns A promise of the type @see {@link IAuthor} containing the author to get.
    */
   public getAuthorById(id: string | number): Promise<IAuthor> {
     return new Promise((resolve: (value: IAuthor) => void, reject: (err: any) => void) => {
-      $.ajax({ method: "GET", url: base_url + "/" + id })
-        .then((data) => { resolve(data); }, (err) => { reject(err) });
+      $.ajax({ method: 'GET', url: base_url + '/' + id })
+        .then((data) => { resolve(data); }, (err) => { reject(err); });
     });
   }
 
 
-/**
+  /**
  * Saves the given author to the backend api.
  * @returns A promise of the type @see {@link IAuthor} containing the autor returned by the backend api.
  */
   public saveAuthor(author: IAuthor): Promise<IAuthor> {
     return new Promise((resolve: (value: IAuthor) => void, reject: (err: any) => void) => {
-       // simulate server save
+      // simulate server save
       window.setTimeout(() => {
         if (Math.random() > 0.8) {
-          reject("Could not save author.");
+          reject('Could not save author.');
         } else {
           resolve(author);
         }
index b56058d..5d9f13a 100644 (file)
@@ -15,7 +15,7 @@
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
+import React from 'react';
 import { withRouter, RouteComponentProps } from 'react-router-dom';
 
 import Table from '@mui/material/Table';
@@ -25,22 +25,28 @@ import TableHead from '@mui/material/TableHead';
 import TableRow from '@mui/material/TableRow';
 import Paper from '@mui/material/Paper'; // means border
 
-import connect from '../../../../framework/src/flux/connect';
+import { connect } from '../../../../framework/src/flux/connect';
 
 import { loadAllAuthorsAsync } from '../actions/authorActions';
 import { IAuthor } from '../models/author';
 
 interface IAuthorsListProps {
-  authors: IAuthor[],
-  busy: boolean,
-  onLoadAllAuthors: () => void
+  authors: IAuthor[];
+  busy: boolean;
+  onLoadAllAuthors: () => void;
 }
 
 class AuthorsListComponent extends React.Component<RouteComponentProps & IAuthorsListProps> {
 
   render(): JSX.Element {
     const { authors, busy } = this.props;
-    return (
+    return busy
+      ? (
+        <Paper>
+          Loading
+        </Paper>
+      )
+      : (
       <Paper>
         <Table padding="normal" >
           <TableHead>
@@ -52,7 +58,7 @@ class AuthorsListComponent extends React.Component<RouteComponentProps & IAuthor
           </TableHead>
           <TableBody>
             {authors.map(author => (
-              <TableRow key={author.id} onClick={(e) => this.editAuthor(author)}>
+              <TableRow key={author.id} onClick={(_e) => this.editAuthor(author)}>
                 <TableCell>{author.id}</TableCell>
                 <TableCell>{author.firstName}</TableCell>
                 <TableCell>{author.lastName}</TableCell>
@@ -61,15 +67,15 @@ class AuthorsListComponent extends React.Component<RouteComponentProps & IAuthor
           </TableBody>
         </Table>
       </Paper>
-    );
-  };
+      );
+  }
 
   public componentDidMount() {
     this.props.onLoadAllAuthors();
   }
 
   private editAuthor = (author: IAuthor) => {
-    author && this.props.history.push(this.props.match.path + '/' + author.id);
+    if (author) this.props.history.push(this.props.match.path + '/' + author.id);
   };
 }
 
@@ -77,11 +83,11 @@ export const AuthorsList = withRouter(
   connect(
     ({ demo: state }) => ({
       authors: state.listAuthors.authors,
-      busy: state.listAuthors.busy
+      busy: state.listAuthors.busy,
     }),
     (dispatcher) => ({
       onLoadAllAuthors: () => {
-        dispatcher.dispatch(loadAllAuthorsAsync)
-      }
+        dispatcher.dispatch(loadAllAuthorsAsync);
+      },
     }))(AuthorsListComponent));
 export default AuthorsList;
index 92f6712..0da619b 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
+import React from 'react';
 import { withRouter, RouteComponentProps } from 'react-router-dom';
 
-type EditAuthorProps = RouteComponentProps<{ authorId: string}>;
+type EditAuthorProps = RouteComponentProps<{ authorId: string }>;
 
 class EditAuthorComponent extends React.Component<EditAuthorProps> {
   render(): JSX.Element {
@@ -26,7 +26,7 @@ class EditAuthorComponent extends React.Component<EditAuthorProps> {
       <div>
         <h2>Edit Author { this.props.match.params.authorId }</h2>
       </div>
-    )
+    );
   }
 }
 
index a66b5d8..ca65092 100644 (file)
@@ -4,7 +4,7 @@
     "outDir": "./dist",
     "sourceMap": true,
     "forceConsistentCasingInFileNames": true,
-    "allowSyntheticDefaultImports": false,
+    "allowSyntheticDefaultImports": true,
     "allowUnreachableCode": false,
     "allowUnusedLabels": false,
     "noFallthroughCasesInSwitch": true,
index 19d9ad0..9e4ed5f 100644 (file)
@@ -19,6 +19,7 @@
   ~ ============LICENSE_END=======================================================
   ~
   -->
+
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
                         <!-- optional: default phase is "generate-resources" -->
                         <phase>initialize</phase>
                         <configuration>
-                            <nodeVersion>v12.13.0</nodeVersion>
+                            <nodeVersion>v12.22.0</nodeVersion>
                             <yarnVersion>v1.22.10</yarnVersion>
                         </configuration>
                     </execution>
diff --git a/sdnr/wt/odlux/apps/eventLogApp/src/assets/icons/eventLogAppIcon.svg b/sdnr/wt/odlux/apps/eventLogApp/src/assets/icons/eventLogAppIcon.svg
new file mode 100644 (file)
index 0000000..743167d
--- /dev/null
@@ -0,0 +1,21 @@
+<!-- highstreet technologies GmbH colour scheme \r
+       Grey    #565656\r
+       LBlue #36A9E1\r
+       DBlue #246DA2\r
+       Green #003F2C / #006C4B\r
+       Yellw #C8D400\r
+       Red     #D81036\r
+-->\r
+\r
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 512 512">\r
+\r
+<g>\r
+<path fill="#C8D400" d="M192,224c-17.672,0-32,14.328-32,32s14.328,32,32,32h128c17.672,0,32-14.328,32-32s-14.328-32-32-32H192z"/>\r
+\r
+<path fill="#C8D400" d="M256,320h-64c-17.672,0-32,14.328-32,32s14.328,32,32,32h64c17.672,0,32-14.328,32-32S273.672,320,256,320z"/>\r
+\r
+<path fill="#565656" d="M416,32h-80c0-17.674-11.938-32-26.668-32H202.668C187.938,0,176,14.326,176,32H96c-17.672,0-32,14.328-32,32v416\r
+               c0,17.672,14.328,32,32,32h320c17.672,0,32-14.328,32-32V64C448,46.328,433.672,32,416,32z M256,32c17.672,0,32,14.326,32,32\r
+               c0,17.673-14.328,32-32,32s-32-14.327-32-32C224,46.326,238.328,32,256,32z M384,448H128V96h48v32h160V96h48V448z"/>\r
+</g>\r
+</svg>\r
index a2edb43..8ee322a 100644 (file)
 */
 // app configuration and main entry point for the app
 
-import * as React from "react";
-import { faBookOpen } from '@fortawesome/free-solid-svg-icons'; // select app icon
+import React, { FC } from 'react';
+
 import applicationManager from '../../../framework/src/services/applicationManager';
 
 import { EventLog } from './views/eventLog';
 import eventLogAppRootHandler from './handlers/eventLogAppRootHandler';
 
-const App : React.SFC = (props) => {
-  return <EventLog />
+const appIcon = require('./assets/icons/eventLogAppIcon.svg');  // select app icon
+
+const App : FC = () => {
+  return <EventLog />;
 };
 
 export function register() {
   applicationManager.registerApplication({
-    name: "eventLog",
-    icon: faBookOpen,
+    name: 'eventLog',
+    icon: appIcon,
     rootActionHandler: eventLogAppRootHandler,
     rootComponent: App,
-    menuEntry: "EventLog"
+    menuEntry: 'EventLog',
   });
 }
 
index a66b5d8..ca65092 100644 (file)
@@ -4,7 +4,7 @@
     "outDir": "./dist",
     "sourceMap": true,
     "forceConsistentCasingInFileNames": true,
-    "allowSyntheticDefaultImports": false,
+    "allowSyntheticDefaultImports": true,
     "allowUnreachableCode": false,
     "allowUnusedLabels": false,
     "noFallthroughCasesInSwitch": true,
index de309c1..3d056ec 100644 (file)
@@ -57,6 +57,16 @@ module.exports = (env) => {
         use: [{
           loader: "babel-loader"
         }]
+      }, {
+        //don't minify images
+        test: /\.(png|gif|jpg|svg)$/,
+        use: [{
+          loader: 'url-loader',
+          options: {
+            limit: 10,
+            name: './images/[name].[ext]'
+          }
+        }]
       }]
     },
 
index a5958d8..9531055 100644 (file)
@@ -26,7 +26,9 @@
     "@mui/icons-material": "^5.2.0",
     "@mui/material": "^5.2.2",
     "@mui/styles": "^5.2.2",
-    "@odlux/framework": "*"
+    "@odlux/framework": "*",
+    "@fortawesome/free-solid-svg-icons": "5.6.3",
+    "@fortawesome/react-fontawesome": "0.1.14"
   },
   "peerDependencies": {
     "@types/classnames": "2.2.6",
@@ -38,6 +40,7 @@
     "jquery": "3.3.1",
     "react": "17.0.2",
     "react-dom": "17.0.2",
-    "react-router-dom": "5.2.0"
+    "react-router-dom": "5.2.0",
+    "react-chartjs-2": "^3.0.3"
   }
 }
index 31a3760..ba4df50 100644 (file)
@@ -19,6 +19,7 @@
   ~ ============LICENSE_END=======================================================
   ~
   -->
+
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
                         <!-- optional: default phase is "generate-resources" -->
                         <phase>initialize</phase>
                         <configuration>
-                            <nodeVersion>v12.13.0</nodeVersion>
+                            <nodeVersion>v12.22.0</nodeVersion>
                             <yarnVersion>v1.22.10</yarnVersion>
                         </configuration>
                     </execution>
index cdcbf64..7aac8ba 100644 (file)
  */
 
 
-import { clearStuckAlarms } from "../services/faultStatusService"
-import { Dispatch } from "../../../../framework/src/flux/store";
-import { FaultApplicationBaseAction } from "./notificationActions";
+import { Dispatch } from '../../../../framework/src/flux/store';
+
+import { clearStuckAlarms } from '../services/faultStatusService';
+import { FaultApplicationBaseAction } from './notificationActions';
 
 export class AreStuckAlarmsCleared extends FaultApplicationBaseAction {
-    constructor(public isBusy: boolean) {
-        super();
-    }
+  constructor(public isBusy: boolean) {
+    super();
+  }
 }
 
 
 export const clearStuckAlarmAsyncAction = (dispatch: Dispatch) => async (nodeNames: string[]) => {
-    dispatch(new AreStuckAlarmsCleared(true));
-    const result = await clearStuckAlarms(nodeNames).catch(error => { console.error(error); return undefined });
-    dispatch(new AreStuckAlarmsCleared(false));
-    return result;
-}
\ No newline at end of file
+  dispatch(new AreStuckAlarmsCleared(true));
+  const result = await clearStuckAlarms(nodeNames).catch(error => { console.error(error); return undefined; });
+  dispatch(new AreStuckAlarmsCleared(false));
+  return result;
+};
\ No newline at end of file
index 7cf02ac..fb29e9c 100644 (file)
@@ -16,6 +16,7 @@
  * ============LICENSE_END==========================================================================
  */
 import { Action } from '../../../../framework/src/flux/action';
+
 import { PanelId } from '../models/panelId';
 
 export class SetPanelAction extends Action {
@@ -32,5 +33,5 @@ export class RememberCurrentPanelAction extends Action {
 
 export const setPanelAction = (panelId: PanelId) => {
   return new SetPanelAction(panelId);
-}
+};
 
index 54fea6a..8b631b9 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import { FaultApplicationBaseAction } from './notificationActions';
-import { getFaultStateFromDatabase } from '../services/faultStatusService';
 import { Dispatch } from '../../../../framework/src/flux/store';
 
+import { getFaultStateFromDatabase } from '../services/faultStatusService';
+import { FaultApplicationBaseAction } from './notificationActions';
+
 
 export class SetFaultStatusAction extends FaultApplicationBaseAction {
   constructor(public criticalFaults: number, public majorFaults: number, public minorFaults: number, public warnings: number,
@@ -32,29 +33,28 @@ export class SetFaultStatusAction extends FaultApplicationBaseAction {
 
 export const refreshFaultStatusAsyncAction = async (dispatch: Dispatch) => {
 
-  dispatch(new SetFaultStatusAction(0, 0, 0, 0, true, 0, 0, 0, 0, 0, 0, 0, 0, true));
+  // dispatch(new SetFaultStatusAction(0, 0, 0, 0, true, 0, 0, 0, 0, 0, 0, 0, 0, true));
   const result = await getFaultStateFromDatabase().catch(_ => null);
   if (result) {
     const statusAction = new SetFaultStatusAction(
-      result["Critical"] || 0,
-      result["Major"] || 0,
-      result["Minor"] || 0,
-      result["Warning"] || 0,
+      result.Critical || 0,
+      result.Major || 0,
+      result.Minor || 0,
+      result.Warning || 0,
+      false,
+      result.Connected || 0,
+      result.Connecting || 0,
+      result.Disconnected || 0,
+      result.Mounted || 0,
+      result.UnableToConnect || 0,
+      result.Undefined || 0,
+      result.Unmounted || 0,
+      result.total || 0,
       false,
-      result["Connected"] || 0,
-      result["Connecting"] || 0,
-      result["Disconnected"] || 0,
-      result["Mounted"] || 0,
-      result["UnableToConnect"] || 0,
-      result["Undefined"] || 0,
-      result["Unmounted"] || 0,
-      result["total"] || 0,
-      false
     );
     dispatch(statusAction);
     return;
-  }
-  else {
+  } else {
     dispatch(new SetFaultStatusAction(0, 0, 0, 0, false, 0, 0, 0, 0, 0, 0, 0, 0, false));
   }
-}
+};
diff --git a/sdnr/wt/odlux/apps/faultApp/src/assets/icons/faultAppIcon.svg b/sdnr/wt/odlux/apps/faultApp/src/assets/icons/faultAppIcon.svg
new file mode 100644 (file)
index 0000000..aabbf4c
--- /dev/null
@@ -0,0 +1,19 @@
+<!-- highstreet technologies GmbH colour scheme 
+       Grey    #565656
+       LBlue #36A9E1
+       DBlue #246DA2
+       Green #003F2C / #006C4B
+       Yellw #C8D400
+       Red     #D81036
+-->
+
+<svg viewBox="0 0 500 540" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" >
+    <g transform="matrix(27.7762,0,0,27.7762,-13.8603,-27.7762)">
+
+ <path fill="#565656" d="M 16.8 15.101 C 15.656 14.242 15 12.929 15 11.5 L 15 8.5 C 15 5.987 13.306 3.862 11 3.208 L 11 2.5 C 11 1.673 10.327 1 9.5 1 C 8.673 1 8 1.673 8 2.5 L 8 3.208 C 5.694 3.863 4 5.987 4 8.5 L 4 11.5 C 4 12.929 3.344 14.241 2.2 15.101 C 2.028 15.23 1.958 15.455 2.026 15.659 C 2.094 15.863 2.285 16.001 2.5 16.001 L 16.499 16.001 C 16.714 16.001 16.905 15.863 16.973 15.659 C 17.041 15.455 16.971 15.23 16.799 15.101 L 16.8 15.101 Z" />
+
+ <path fill="#D81036" d="m 7.05 17.001 c -0.033 0.164 -0.051 0.331 -0.051 0.5 c 0 1.378 1.122 2.5 2.5 2.5 c 1.378 0 2.5 -1.122 2.5 -2.5 c 0 -0.168 -0.017 -0.336 -0.05 -0.5 l -4.899 0 z" />
+
+
+    </g>
+</svg>
index 463c207..e86b756 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
-import * as React from 'react'
-import { DialogContent, DialogActions, Button, Dialog, DialogTitle, DialogContentText } from '@mui/material';
-import { currentProblemsReloadAction } from '../handlers/currentProblemsHandler';
+import React from 'react';
+
+import { Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle } from '@mui/material';
+
+import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect';
+
 import { clearStuckAlarmAsyncAction } from '../actions/clearStuckAlarmsAction';
-import connect, { IDispatcher, Connect } from '../../../../framework/src/flux/connect';
+import { currentAlarmsReloadAction } from '../handlers/currentAlarmsHandler';
 
 export enum ClearStuckAlarmsDialogMode {
-    None = "none",
-    Show = "show"
+  None = 'none',
+  Show = 'show',
 }
 
 const mapDispatch = (dispatcher: IDispatcher) => ({
-    clearStuckAlarmsAsync: clearStuckAlarmAsyncAction(dispatcher.dispatch),
-    reloadCurrentProblemsAction: () => dispatcher.dispatch(currentProblemsReloadAction)
+  clearStuckAlarmsAsync: clearStuckAlarmAsyncAction(dispatcher.dispatch),
+  reloadCurrentAlarmsAction: () => dispatcher.dispatch(currentAlarmsReloadAction),
 });
 
 type clearStuckAlarmsProps = Connect<typeof undefined, typeof mapDispatch> & {
-    numberDevices: Number;
-    mode: ClearStuckAlarmsDialogMode;
-    stuckAlarms: string[];
-    onClose: () => void;
-}
+  numberDevices: Number;
+  mode: ClearStuckAlarmsDialogMode;
+  stuckAlarms: string[];
+  onClose: () => void;
+};
 
 type ClearStuckAlarmsState = {
-    clearAlarmsSuccessful: boolean;
-    errormessage: string;
-    unclearedAlarms: string[];
-}
-
-class ClearStuckAlarmsDialogComponent extends React.Component<clearStuckAlarmsProps, ClearStuckAlarmsState>{
-    constructor(props: clearStuckAlarmsProps) {
-        super(props);
-        this.state = { 
-            clearAlarmsSuccessful: true, 
-            errormessage: '', 
-            unclearedAlarms: [] 
-        };
-    }
-
-    onClose = (event: React.MouseEvent) => {
-        event.stopPropagation();
-        event.preventDefault();
-        this.props.onClose();
+  clearAlarmsSuccessful: boolean;
+  errormessage: string;
+  unclearedAlarms: string[];
+};
+
+class ClearStuckAlarmsDialogComponent extends React.Component<clearStuckAlarmsProps, ClearStuckAlarmsState> {
+  constructor(props: clearStuckAlarmsProps) {
+    super(props);
+    this.state = {
+      clearAlarmsSuccessful: true,
+      errormessage: '',
+      unclearedAlarms: [],
+    };
+  }
+
+  onClose = (event: React.MouseEvent) => {
+    event.stopPropagation();
+    event.preventDefault();
+    this.props.onClose();
+  };
+
+  onRefresh = async (event: React.MouseEvent) => {
+    event.stopPropagation();
+    event.preventDefault();
+    const result = await this.props.clearStuckAlarmsAsync(this.props.stuckAlarms);
+
+    if (result && result['devicemanager:output'].nodenames && result['devicemanager:output'].nodenames.length !== this.props.stuckAlarms.length) { //show errormessage if not all devices were cleared
+      const undeletedAlarm = this.props.stuckAlarms.filter(item => !result['devicemanager:output'].nodenames.includes(item));
+      const error = 'The alarms of the following devices couldn\'t be refreshed: ';
+      this.setState({ clearAlarmsSuccessful: false, errormessage: error, unclearedAlarms: undeletedAlarm });
+      return;
+
+    } else { //show errormessage if no devices were cleared
+      this.setState({ clearAlarmsSuccessful: false, errormessage: 'Alarms couldn\'t be refreshed.', unclearedAlarms: [] });
     }
 
-    onRefresh = async (event: React.MouseEvent) => {
-        event.stopPropagation();
-        event.preventDefault();
-        const result = await this.props.clearStuckAlarmsAsync(this.props.stuckAlarms);
-
-        if (result && result["devicemanager:output"].nodenames && result["devicemanager:output"].nodenames.length !== this.props.stuckAlarms.length) { //show errormessage if not all devices were cleared
-            const undeletedAlarm = this.props.stuckAlarms.filter(item => !result["devicemanager:output"].nodenames.includes(item));
-            const error = "The alarms of the following devices couldn't be refreshed: ";
-            this.setState({ clearAlarmsSuccessful: false, errormessage: error, unclearedAlarms: undeletedAlarm });
-            return;
-
-        } else { //show errormessage if no devices were cleared
-            this.setState({ clearAlarmsSuccessful: false, errormessage: "Alarms couldn't be refreshed.", unclearedAlarms: [] });
-        }
-
-        this.props.reloadCurrentProblemsAction();
-        this.props.onClose();
-    }
-
-    onOk = (event: React.MouseEvent) => {
-
-        event.stopPropagation();
-        event.preventDefault();
-        if (this.state.unclearedAlarms.length > 0)
-            this.props.reloadCurrentProblemsAction();
-        this.props.onClose();
-    }
-
-    render() {
-        console.log(this.props.stuckAlarms);
-        const device = this.props.numberDevices > 1 ? 'devices' : 'device'
-        const defaultMessage = "Are you sure you want to refresh all alarms for " + this.props.numberDevices + " " + device + "?"
-        const message = this.state.clearAlarmsSuccessful ? defaultMessage : this.state.errormessage;
-
-        const defaultTitle = "Refresh Confirmation"
-        const title = this.state.clearAlarmsSuccessful ? defaultTitle : 'Refresh Result';
-
-        return (
-            <Dialog open={this.props.mode !== ClearStuckAlarmsDialogMode.None}>
-                <DialogTitle>{title}</DialogTitle>
-                <DialogContent>
-                    <DialogContentText>
-                        {message}
-                    </DialogContentText>
-                    {
-                        this.state.unclearedAlarms.map(item =>
-                            <DialogContentText>
-                                {item}
-                            </DialogContentText>
-                        )
-                    }
-                </DialogContent>
-                <DialogActions>
-                    {
-                        this.state.clearAlarmsSuccessful &&
-                        <>
-                            <Button color="inherit" onClick={this.onRefresh}>Yes</Button>
-                            <Button color="inherit" onClick={this.onClose}>No</Button>
-                        </>
-                    }
-
-                    {
-                        !this.state.clearAlarmsSuccessful && <Button color="inherit" onClick={this.onOk}>Ok</Button>
-                    }
-                </DialogActions>
-            </Dialog>
-        )
-    }
+    this.props.reloadCurrentAlarmsAction();
+    this.props.onClose();
+  };
+
+  onOk = (event: React.MouseEvent) => {
+
+    event.stopPropagation();
+    event.preventDefault();
+    if (this.state.unclearedAlarms.length > 0)
+      this.props.reloadCurrentAlarmsAction();
+    this.props.onClose();
+  };
+
+  render() {
+    console.log(this.props.stuckAlarms);
+    const device = this.props.numberDevices > 1 ? 'devices' : 'device';
+    const defaultMessage = 'Are you sure you want to refresh all alarms for ' + this.props.numberDevices + ' ' + device + '?';
+    const message = this.state.clearAlarmsSuccessful ? defaultMessage : this.state.errormessage;
+
+    const defaultTitle = 'Refresh Confirmation';
+    const title = this.state.clearAlarmsSuccessful ? defaultTitle : 'Refresh Result';
+
+    return (
+      <Dialog open={this.props.mode !== ClearStuckAlarmsDialogMode.None}>
+        <DialogTitle>{title}</DialogTitle>
+        <DialogContent>
+          <DialogContentText>
+            {message}
+          </DialogContentText>
+          {
+            this.state.unclearedAlarms.map(item =>
+              <DialogContentText>
+                {item}
+              </DialogContentText>,
+            )
+          }
+        </DialogContent>
+        <DialogActions>
+          {
+            this.state.clearAlarmsSuccessful &&
+            <>
+              <Button color="inherit" onClick={this.onRefresh}>Yes</Button>
+              <Button color="inherit" onClick={this.onClose}>No</Button>
+            </>
+          }
+
+          {
+            !this.state.clearAlarmsSuccessful && <Button color="inherit" onClick={this.onOk}>Ok</Button>
+          }
+        </DialogActions>
+      </Dialog>
+    );
+  }
 }
 
 const ClearStuckAlarmsDialog = connect(undefined, mapDispatch)(ClearStuckAlarmsDialogComponent);
index a817059..a3e32c4 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
+import React from 'react';
+import { RouteComponentProps, withRouter } from 'react-router-dom';
 
-import { withRouter, RouteComponentProps } from 'react-router-dom';
-import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect';;
-import { Theme } from '@mui/material';
 import { WithStyles } from '@mui/styles';
 import createStyles from '@mui/styles/createStyles';
-import withStyles from '@mui/styles/withStyles';
 import { Doughnut } from 'react-chartjs-2';
-import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
+import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect';
+
 import { NavigateToApplication } from '../../../../framework/src/actions/navigationActions';
+import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
 
-const styles = (theme: Theme) => createStyles({
+const styles = () => createStyles({
   pageWidthSettings: {
     width: '50%',
-    float: 'left'
+    float: 'left',
   },
-})
+});
 
-const scrollbar = { overflow: "auto", paddingRight: "20px" }
+const scrollbar = { overflow: 'auto', paddingRight: '20px' };
 
 let connectionStatusinitialLoad = true;
 let connectionStatusinitialStateChanged = false;
@@ -47,7 +46,7 @@ let alarmStatusDataLoad: number[] = [0, 0, 0, 0];
 let alarmTotalCount = 0;
 
 const mapProps = (state: IApplicationStoreState) => ({
-  alarmStatus: state.fault.faultStatus
+  alarmStatus: state.fault.faultStatus,
 });
 
 const mapDispatch = (dispatcher: IDispatcher) => ({
@@ -60,11 +59,10 @@ class DashboardHome extends React.Component<HomeComponentProps>  {
   constructor(props: HomeComponentProps) {
     super(props);
     this.state = {
-    }
+    };
   }
 
   render(): JSX.Element {
-    const { classes } = this.props;
 
     if (!this.props.alarmStatus.isLoadingConnectionStatusChart) {
       connectionStatusDataLoad = [
@@ -72,7 +70,7 @@ class DashboardHome extends React.Component<HomeComponentProps>  {
         this.props.alarmStatus.Connecting,
         this.props.alarmStatus.Disconnected,
         this.props.alarmStatus.UnableToConnect,
-        this.props.alarmStatus.Undefined
+        this.props.alarmStatus.Undefined,
       ];
       connectionTotalCount = this.props.alarmStatus.Connected + this.props.alarmStatus.Connecting
         + this.props.alarmStatus.Disconnected + this.props.alarmStatus.UnableToConnect + this.props.alarmStatus.Undefined;
@@ -84,7 +82,7 @@ class DashboardHome extends React.Component<HomeComponentProps>  {
         this.props.alarmStatus.critical,
         this.props.alarmStatus.major,
         this.props.alarmStatus.minor,
-        this.props.alarmStatus.warning
+        this.props.alarmStatus.warning,
       ];
       alarmTotalCount = this.props.alarmStatus.critical + this.props.alarmStatus.major
         + this.props.alarmStatus.minor + this.props.alarmStatus.warning;
@@ -97,7 +95,7 @@ class DashboardHome extends React.Component<HomeComponentProps>  {
         'Connecting: ' + this.props.alarmStatus.Connecting,
         'Disconnected: ' + this.props.alarmStatus.Disconnected,
         'UnableToConnect: ' + this.props.alarmStatus.UnableToConnect,
-        'Undefined: ' + this.props.alarmStatus.Undefined
+        'Undefined: ' + this.props.alarmStatus.Undefined,
       ],
       datasets: [{
         labels: ['Connected', 'Connecting', 'Disconnected', 'UnableToConnect', 'Undefined'],
@@ -107,9 +105,9 @@ class DashboardHome extends React.Component<HomeComponentProps>  {
           'rgb(255, 102, 0)',
           'rgb(191, 191, 191)',
           'rgb(191, 191, 191)',
-          'rgb(242, 240, 240)'
-        ]
-      }]
+          'rgb(242, 240, 240)',
+        ],
+      }],
     };
 
 
@@ -119,9 +117,9 @@ class DashboardHome extends React.Component<HomeComponentProps>  {
       datasets: [{
         data: [1],
         backgroundColor: [
-          'rgb(255, 255, 255)'
-        ]
-      }]
+          'rgb(255, 255, 255)',
+        ],
+      }],
     };
 
     /** Loading Connection Status chart */
@@ -130,9 +128,9 @@ class DashboardHome extends React.Component<HomeComponentProps>  {
       datasets: [{
         data: [1],
         backgroundColor: [
-          'rgb(255, 255, 255)'
-        ]
-      }]
+          'rgb(255, 255, 255)',
+        ],
+      }],
     };
 
     /** Loading Alarm Status chart */
@@ -141,9 +139,9 @@ class DashboardHome extends React.Component<HomeComponentProps>  {
       datasets: [{
         data: [1],
         backgroundColor: [
-          'rgb(255, 255, 255)'
-        ]
-      }]
+          'rgb(255, 255, 255)',
+        ],
+      }],
     };
 
     /** Connection status options */
@@ -155,57 +153,57 @@ class DashboardHome extends React.Component<HomeComponentProps>  {
             let label =
               (data.datasets[tooltipItem.datasetIndex].labels &&
                 data.datasets[tooltipItem.datasetIndex].labels[
-                tooltipItem.index
+                  tooltipItem.index
                 ]) ||
               data.labels[tooltipItem.index] ||
-              "";
+              '';
             if (label) {
-              label += ": ";
+              label += ': ';
             }
             label +=
               data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index] +
-              (data.datasets[tooltipItem.datasetIndex].labelSuffix || "");
+              (data.datasets[tooltipItem.datasetIndex].labelSuffix || '');
 
             return label;
-          }
-        }
+          },
+        },
       },
       responsive: true,
       maintainAspectRatio: false,
       animation: {
-        duration: 0
+        duration: 0,
       },
       plugins: {
         legend: {
           display: true,
-          position: 'top'
-        }
+          position: 'top',
+        },
       },
       onClick: (event: MouseEvent, item: any) => {
         if (item[0]) {
           let connectionStatus = labels[item[0]._index] + '';
-          this.props.navigateToApplication("connect", '/connectionStatus/' + connectionStatus);
+          this.props.navigateToApplication('connect', '/connectionStatus/' + connectionStatus);
         }
-      }
-    }
+      },
+    };
 
     /** Connection status unavailable options */
     const connectionStatusUnavailableOptions = {
       responsive: true,
       maintainAspectRatio: false,
       animation: {
-        duration: 0
+        duration: 0,
       },
       plugins: {
         legend: {
           display: true,
-          position: 'top'
+          position: 'top',
         },
         tooltip: {
-          enabled: false
-        }
-      }
-    }
+          enabled: false,
+        },
+      },
+    };
 
     /** Add text inside the doughnut chart for Connection Status */
     const connectionStatusPlugins = [{
@@ -215,15 +213,15 @@ class DashboardHome extends React.Component<HomeComponentProps>  {
           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",
+        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 = {
@@ -231,7 +229,7 @@ class DashboardHome extends React.Component<HomeComponentProps>  {
         'Critical : ' + this.props.alarmStatus.critical,
         'Major : ' + this.props.alarmStatus.major,
         'Minor : ' + this.props.alarmStatus.minor,
-        'Warning : ' + this.props.alarmStatus.warning
+        'Warning : ' + this.props.alarmStatus.warning,
       ],
       datasets: [{
         labels: ['Critical', 'Major', 'Minor', 'Warning'],
@@ -240,10 +238,10 @@ class DashboardHome extends React.Component<HomeComponentProps>  {
           'rgb(240, 25, 10)',
           'rgb(240, 133, 10)',
           'rgb(240, 240, 10)',
-          'rgb(46, 115, 176)'
+          'rgb(46, 115, 176)',
         ],
-      }]
-    }
+      }],
+    };
 
     /** No Alarm status available */
     const alarmStatusUnavailableData = {
@@ -251,9 +249,9 @@ class DashboardHome extends React.Component<HomeComponentProps>  {
       datasets: [{
         data: [1],
         backgroundColor: [
-          'rgb(0, 153, 51)'
-        ]
-      }]
+          'rgb(0, 153, 51)',
+        ],
+      }],
     };
 
     /** Alarm status Options */
@@ -265,36 +263,36 @@ class DashboardHome extends React.Component<HomeComponentProps>  {
             let label =
               (data.datasets[tooltipItem.datasetIndex].labels &&
                 data.datasets[tooltipItem.datasetIndex].labels[
-                tooltipItem.index
+                  tooltipItem.index
                 ]) ||
               data.labels[tooltipItem.index] ||
-              "";
+              '';
             if (label) {
-              label += ": ";
+              label += ': ';
             }
             label +=
               data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index] +
-              (data.datasets[tooltipItem.datasetIndex].labelSuffix || "");
+              (data.datasets[tooltipItem.datasetIndex].labelSuffix || '');
 
             return label;
-          }
-        }
+          },
+        },
       },
       responsive: true,
       maintainAspectRatio: false,
       animation: {
-        duration: 0
+        duration: 0,
       },
       plugins: {
         legend: {
           display: true,
-          position: 'top'
-        }
+          position: 'top',
+        },
       },
       onClick: (event: MouseEvent, item: any) => {
         if (item[0]) {
           let severity = alarmLabels[item[0]._index] + '';
-          this.props.navigateToApplication("fault", '/alarmStatus/' + severity);
+          this.props.navigateToApplication('fault', '/alarmStatus/' + severity);
         }
       },
     };
@@ -304,18 +302,18 @@ class DashboardHome extends React.Component<HomeComponentProps>  {
       responsive: true,
       maintainAspectRatio: false,
       animation: {
-        duration: 0
+        duration: 0,
       },
       plugins: {
         legend: {
           display: true,
-          position: 'top'
+          position: 'top',
         },
         tooltip: {
-          enabled: false
-        }
-      }
-    }
+          enabled: false,
+        },
+      },
+    };
     /** Add text inside the doughnut chart for Alarm Status */
     const alarmStatusPlugins = [{
       beforeDraw: function (chart: any) {
@@ -324,15 +322,15 @@ class DashboardHome extends React.Component<HomeComponentProps>  {
           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",
+        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 (
       <>
@@ -397,7 +395,7 @@ class DashboardHome extends React.Component<HomeComponentProps>  {
           </div>
         </div>
       </>
-    )
+    );
   }
 
   /** Check if connection status data available */
@@ -412,7 +410,7 @@ class DashboardHome extends React.Component<HomeComponentProps>  {
     } else {
       return true;
     }
-  }
+  };
 
   /** Check if connection status chart data is loaded */
   public checkElementsAreLoaded = () => {
@@ -434,7 +432,7 @@ class DashboardHome extends React.Component<HomeComponentProps>  {
       return !isLoadingCheck.isLoadingConnectionStatusChart;
     }
     return true;
-  }
+  };
 
   /** Check if alarms data available */
   public checkAlarmStatus = () => {
@@ -444,11 +442,10 @@ class DashboardHome extends React.Component<HomeComponentProps>  {
     }
     if (alarmCount.critical == 0 && alarmCount.major == 0 && alarmCount.minor == 0 && alarmCount.warning == 0) {
       return false;
-    }
-    else {
+    } else {
       return true;
     }
-  }
+  };
 
   /** Check if alarm status chart data is loaded */
   public checkAlarmsAreLoaded = () => {
@@ -470,7 +467,7 @@ class DashboardHome extends React.Component<HomeComponentProps>  {
       return !isLoadingCheck.isLoadingAlarmStatusChart;
     }
     return true;
-  }
+  };
 }
 
 export default (withRouter(connect(mapProps, mapDispatch)(DashboardHome)));
\ No newline at end of file
index 7820dfd..c2ca4a5 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
+import React from 'react';
 
-import { Theme } from '@mui/material/styles';
+import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; // select app icon
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import Typography from '@mui/material/Typography';
 import { WithStyles } from '@mui/styles';
-import withStyles from '@mui/styles/withStyles';
 import createStyles from '@mui/styles/createStyles';
-import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';  // select app icon
+import withStyles from '@mui/styles/withStyles';
 
-import connect, { Connect } from '../../../../framework/src/flux/connect';
+import { connect, Connect } from '../../../../framework/src/flux/connect';
 import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
 
-import Typography from '@mui/material/Typography';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 
-const styles = (theme: Theme) => createStyles({
+const styles = () => createStyles({
   icon: {
     marginLeft: 8,
-    marginRight: 8
+    marginRight: 8,
   },
   critical: {
-    color: "red"
+    color: 'red',
   },
   major: {
-    color: "orange"
+    color: 'orange',
   },
   minor: {
-    color: "#f7f700"
+    color: '#f7f700',
   },
   warning: {
-    color: "#428bca"
-  }
+    color: '#428bca',
+  },
 });
 
 const mapProps = (state: IApplicationStoreState) => ({
@@ -75,7 +74,7 @@ class FaultStatusComponent extends React.Component<FaultStatusComponentProps> {
       </Typography>
       </>
     );
-  };
+  }
 }
 
 export const FaultStatus = withStyles(styles)(connect(mapProps)(FaultStatusComponent));
index 8c639ee..59657d8 100644 (file)
@@ -24,78 +24,73 @@ import DialogContent from '@mui/material/DialogContent';
 import DialogContentText from '@mui/material/DialogContentText';
 import DialogTitle from '@mui/material/DialogTitle';
 
+import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect';
 import { alarmLogEntriesReloadAction } from '../handlers/alarmLogEntriesHandler';
-import { IDispatcher, connect, Connect } from '../../../../framework/src/flux/connect';
-
 import { Fault } from '../models/fault';
 
 export enum RefreshAlarmLogDialogMode {
-  None = "none",
-  RefreshAlarmLogTable = "RefreshAlarmLogTable",
+  None = 'none',
+  RefreshAlarmLogTable = 'RefreshAlarmLogTable',
 }
 
 const mapDispatch = (dispatcher: IDispatcher) => ({
-  refreshAlarmLog: () => dispatcher.dispatch(alarmLogEntriesReloadAction)
+  refreshAlarmLog: () => dispatcher.dispatch(alarmLogEntriesReloadAction),
 });
 
 type DialogSettings = {
-  dialogTitle: string,
-  dialogDescription: string,
-  applyButtonText: string,
-  cancelButtonText: string,
-  enableMountIdEditor: boolean,
-  enableUsernameEditor: boolean,
-  enableExtendedEditor: boolean,
-}
+  dialogTitle: string;
+  dialogDescription: string;
+  applyButtonText: string;
+  cancelButtonText: string;
+  enableMountIdEditor: boolean;
+  enableUsernameEditor: boolean;
+  enableExtendedEditor: boolean;
+};
 
 const settings: { [key: string]: DialogSettings } = {
   [RefreshAlarmLogDialogMode.None]: {
-    dialogTitle: "",
-    dialogDescription: "",
-    applyButtonText: "",
-    cancelButtonText: "",
+    dialogTitle: '',
+    dialogDescription: '',
+    applyButtonText: '',
+    cancelButtonText: '',
     enableMountIdEditor: false,
     enableUsernameEditor: false,
     enableExtendedEditor: false,
   },
   [RefreshAlarmLogDialogMode.RefreshAlarmLogTable]: {
-    dialogTitle: "Do you want to refresh the Alarm Log?",
-    dialogDescription: "",
-    applyButtonText: "Yes",
-    cancelButtonText: "Cancel",
+    dialogTitle: 'Do you want to refresh the Alarm Log?',
+    dialogDescription: '',
+    applyButtonText: 'Yes',
+    cancelButtonText: 'Cancel',
     enableMountIdEditor: true,
     enableUsernameEditor: true,
     enableExtendedEditor: true,
-  }
-}
+  },
+};
 
 type RefreshAlarmLogDialogComponentProps = Connect<undefined, typeof mapDispatch> & {
   mode: RefreshAlarmLogDialogMode;
   onClose: () => void;
 };
 
-type RefreshAlarmLogDialogComponentState = Fault & { isNameValid: boolean, isHostSet: boolean };
+type RefreshAlarmLogDialogComponentState = Fault & { isNameValid: boolean; isHostSet: boolean };
 
 class RefreshAlarmLogDialogComponent extends React.Component<RefreshAlarmLogDialogComponentProps, RefreshAlarmLogDialogComponentState> {
-  constructor(props: RefreshAlarmLogDialogComponentProps) {
-    super(props);
-  }
-
   render(): JSX.Element {
     const setting = settings[this.props.mode];
     return (
       <Dialog open={this.props.mode !== RefreshAlarmLogDialogMode.None}>
-        <DialogTitle id="form-dialog-title" aria-label={`${setting.dialogTitle.replace(/ /g, "-").toLowerCase()}-dialog`}>{setting.dialogTitle}</DialogTitle>
+        <DialogTitle id="form-dialog-title" aria-label={`${setting.dialogTitle.replace(/ /g, '-').toLowerCase()}-dialog`}>{setting.dialogTitle}</DialogTitle>
         <DialogContent>
           <DialogContentText>
             {setting.dialogDescription}
           </DialogContentText>
         </DialogContent>
         <DialogActions>
-          <Button aria-label="dialog-confirm-button" onClick={(event) => {
+          <Button aria-label="dialog-confirm-button" onClick={() => {
             this.onRefresh();
           }} color="inherit" > {setting.applyButtonText} </Button>
-          <Button aria-label="dialog-cancel-button" onClick={(event) => {
+          <Button aria-label="dialog-cancel-button" onClick={() => {
             this.onCancel();
           }} color="secondary"> {setting.cancelButtonText} </Button>
         </DialogActions>
@@ -110,7 +105,7 @@ class RefreshAlarmLogDialogComponent extends React.Component<RefreshAlarmLogDial
 
   private onCancel = () => {
     this.props.onClose();
-  }
+  };
 }
 
 export const RefreshAlarmLogDialog = connect(undefined, mapDispatch)(RefreshAlarmLogDialogComponent);
diff --git a/sdnr/wt/odlux/apps/faultApp/src/components/refreshCurrentAlarmsDialog.tsx b/sdnr/wt/odlux/apps/faultApp/src/components/refreshCurrentAlarmsDialog.tsx
new file mode 100644 (file)
index 0000000..20cd514
--- /dev/null
@@ -0,0 +1,113 @@
+/**
+ * ============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 Button from '@mui/material/Button';
+import Dialog from '@mui/material/Dialog';
+import DialogActions from '@mui/material/DialogActions';
+import DialogContent from '@mui/material/DialogContent';
+import DialogContentText from '@mui/material/DialogContentText';
+import DialogTitle from '@mui/material/DialogTitle';
+
+import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect';
+
+import { currentAlarmsReloadAction } from '../handlers/currentAlarmsHandler';
+import { Fault } from '../models/fault';
+
+export enum RefreshCurrentAlarmsDialogMode {
+  None = 'none',
+  RefreshCurrentAlarmsTable = 'RefreshCurrentAlarmsTable',
+}
+
+const mapDispatch = (dispatcher: IDispatcher) => ({
+  refreshCurrentAlarms: () => dispatcher.dispatch(currentAlarmsReloadAction),
+});
+
+type DialogSettings = {
+  dialogTitle: string;
+  dialogDescription: string;
+  applyButtonText: string;
+  cancelButtonText: string;
+  enableMountIdEditor: boolean;
+  enableUsernameEditor: boolean;
+  enableExtendedEditor: boolean;
+};
+
+const settings: { [key: string]: DialogSettings } = {
+  [RefreshCurrentAlarmsDialogMode.None]: {
+    dialogTitle: '',
+    dialogDescription: '',
+    applyButtonText: '',
+    cancelButtonText: '',
+    enableMountIdEditor: false,
+    enableUsernameEditor: false,
+    enableExtendedEditor: false,
+  },
+  [RefreshCurrentAlarmsDialogMode.RefreshCurrentAlarmsTable]: {
+    dialogTitle: 'Do you want to refresh the Current Alarms List?',
+    dialogDescription: '',
+    applyButtonText: 'Yes',
+    cancelButtonText: 'Cancel',
+    enableMountIdEditor: true,
+    enableUsernameEditor: true,
+    enableExtendedEditor: true,
+  },
+};
+
+type RefreshCurrentAlarmsDialogComponentProps = Connect<undefined, typeof mapDispatch> & {
+  mode: RefreshCurrentAlarmsDialogMode;
+  onClose: () => void;
+};
+
+type RefreshCurrentAlarmsDialogComponentState = Fault & { isNameValid: boolean; isHostSet: boolean };
+
+class RefreshCurrentAlarmsDialogComponent extends React.Component<RefreshCurrentAlarmsDialogComponentProps, RefreshCurrentAlarmsDialogComponentState> {
+  render(): JSX.Element {
+    const setting = settings[this.props.mode];
+    return (
+      <Dialog open={this.props.mode !== RefreshCurrentAlarmsDialogMode.None}>
+        <DialogTitle id="form-dialog-title" aria-label={`${setting.dialogTitle.replace(/ /g, '-').toLowerCase()}-dialog`}>{setting.dialogTitle}</DialogTitle>
+        <DialogContent>
+          <DialogContentText>
+            {setting.dialogDescription}
+          </DialogContentText>
+        </DialogContent>
+        <DialogActions>
+          <Button aria-label="dialog-confirm-button" onClick={() => {
+            this.onRefresh();
+          }} color="inherit" > {setting.applyButtonText} </Button>
+          <Button aria-label="dialog-cancel-button" onClick={() => {
+            this.onCancel();
+          }} color="secondary"> {setting.cancelButtonText} </Button>
+        </DialogActions>
+      </Dialog>
+    );
+  }
+
+  private onRefresh = () => {
+    this.props.refreshCurrentAlarms();
+    this.props.onClose();
+  };
+
+  private onCancel = () => {
+    this.props.onClose();
+  };
+}
+
+export const RefreshCurrentAlarmsDialog = connect(undefined, mapDispatch)(RefreshCurrentAlarmsDialogComponent);
+export default RefreshCurrentAlarmsDialog;
\ No newline at end of file
diff --git a/sdnr/wt/odlux/apps/faultApp/src/components/refreshCurrentProblemsDialog.tsx b/sdnr/wt/odlux/apps/faultApp/src/components/refreshCurrentProblemsDialog.tsx
deleted file mode 100644 (file)
index e501ec0..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-/**
- * ============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 Button from '@mui/material/Button';
-import Dialog from '@mui/material/Dialog';
-import DialogActions from '@mui/material/DialogActions';
-import DialogContent from '@mui/material/DialogContent';
-import DialogContentText from '@mui/material/DialogContentText';
-import DialogTitle from '@mui/material/DialogTitle';
-
-import { currentProblemsReloadAction } from '../handlers/currentProblemsHandler';
-import { IDispatcher, connect, Connect } from '../../../../framework/src/flux/connect';
-
-import { Fault } from '../models/fault';
-
-export enum RefreshCurrentProblemsDialogMode {
-    None = "none",
-    RefreshCurrentProblemsTable = "RefreshCurrentProblemsTable",
-}
-
-const mapDispatch = (dispatcher: IDispatcher) => ({
-    refreshCurrentProblems: () => dispatcher.dispatch(currentProblemsReloadAction)
-});
-
-type DialogSettings = {
-    dialogTitle: string,
-    dialogDescription: string,
-    applyButtonText: string,
-    cancelButtonText: string,
-    enableMountIdEditor: boolean,
-    enableUsernameEditor: boolean,
-    enableExtendedEditor: boolean,
-}
-
-const settings: { [key: string]: DialogSettings } = {
-    [RefreshCurrentProblemsDialogMode.None]: {
-        dialogTitle: "",
-        dialogDescription: "",
-        applyButtonText: "",
-        cancelButtonText: "",
-        enableMountIdEditor: false,
-        enableUsernameEditor: false,
-        enableExtendedEditor: false,
-    },
-    [RefreshCurrentProblemsDialogMode.RefreshCurrentProblemsTable]: {
-        dialogTitle: "Do you want to refresh the Current Problems List?",
-        dialogDescription: "",
-        applyButtonText: "Yes",
-        cancelButtonText: "Cancel",
-        enableMountIdEditor: true,
-        enableUsernameEditor: true,
-        enableExtendedEditor: true,
-    }
-}
-
-type RefreshCurrentProblemsDialogComponentProps = Connect<undefined, typeof mapDispatch> & {
-    mode: RefreshCurrentProblemsDialogMode;
-    onClose: () => void;
-};
-
-type RefreshCurrentProblemsDialogComponentState = Fault & { isNameValid: boolean, isHostSet: boolean };
-
-class RefreshCurrentProblemsDialogComponent extends React.Component<RefreshCurrentProblemsDialogComponentProps, RefreshCurrentProblemsDialogComponentState> {
-    constructor(props: RefreshCurrentProblemsDialogComponentProps) {
-        super(props);
-    }
-
-    render(): JSX.Element {
-        const setting = settings[this.props.mode];
-        return (
-            <Dialog open={this.props.mode !== RefreshCurrentProblemsDialogMode.None}>
-                <DialogTitle id="form-dialog-title" aria-label={`${setting.dialogTitle.replace(/ /g, "-").toLowerCase()}-dialog`}>{setting.dialogTitle}</DialogTitle>
-                <DialogContent>
-                    <DialogContentText>
-                        {setting.dialogDescription}
-                    </DialogContentText>
-                </DialogContent>
-                <DialogActions>
-                    <Button aria-label="dialog-confirm-button" onClick={(event) => {
-                        this.onRefresh();
-                    }} color="inherit" > {setting.applyButtonText} </Button>
-                    <Button aria-label="dialog-cancel-button" onClick={(event) => {
-                        this.onCancel();
-                    }} color="secondary"> {setting.cancelButtonText} </Button>
-                </DialogActions>
-            </Dialog>
-        );
-    }
-
-    private onRefresh = () => {
-        this.props.refreshCurrentProblems();
-        this.props.onClose();
-    };
-
-    private onCancel = () => {
-        this.props.onClose();
-    }
-}
-
-export const RefreshCurrentProblemsDialog = connect(undefined, mapDispatch)(RefreshCurrentProblemsDialogComponent);
-export default RefreshCurrentProblemsDialog;
\ No newline at end of file
index 31b8259..bdd4596 100644 (file)
@@ -15,7 +15,7 @@
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import { createExternal,IExternalTableState } from '../../../../framework/src/components/material-table/utilities';
+import { createExternal, IExternalTableState } from '../../../../framework/src/components/material-table/utilities';
 import { createSearchDataHandler } from '../../../../framework/src/utilities/elasticSearch';
 
 import { Fault } from '../models/fault';
index 14634b4..0d5a8c7 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
-import { IActionHandler } from "../../../../framework/src/flux/action"
-import { AreStuckAlarmsCleared } from "../actions/clearStuckAlarmsAction";
+import { IActionHandler } from '../../../../framework/src/flux/action';
+
+import { AreStuckAlarmsCleared } from '../actions/clearStuckAlarmsAction';
 
 export interface IStuckAlarms {
-    areAlarmsCleared: boolean
+  areAlarmsCleared: boolean;
 }
 
 const initialState: IStuckAlarms = {
-    areAlarmsCleared: false
-}
+  areAlarmsCleared: false,
+};
 
 export const stuckAlarmHandler: IActionHandler<IStuckAlarms> = (state = initialState, action) => {
-    if (action instanceof AreStuckAlarmsCleared) {
-        state = { ...state, areAlarmsCleared: action.isBusy }
-    }
+  if (action instanceof AreStuckAlarmsCleared) {
+    state = { ...state, areAlarmsCleared: action.isBusy };
+  }
 
-    return state;
-}
\ No newline at end of file
+  return state;
+};
\ No newline at end of file
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import { createExternal,IExternalTableState } from '../../../../framework/src/components/material-table/utilities';
+import { createExternal, IExternalTableState } from '../../../../framework/src/components/material-table/utilities';
 import { createSearchDataHandler } from '../../../../framework/src/utilities/elasticSearch';
 
 import { Fault } from '../models/fault';
 
-export interface ICurrentProblemsState extends IExternalTableState<Fault> { }
+export interface ICurrentAlarmsState extends IExternalTableState<Fault> { }
 
 // create eleactic search data fetch handler
-const currentProblemsSearchHandler = createSearchDataHandler<Fault>('faultcurrent');
+const currentAlarmsSearchHandler = createSearchDataHandler<Fault>('faultcurrent');
 
 export const {
-  actionHandler: currentProblemsActionHandler,
-  createActions: createCurrentProblemsActions,
-  createProperties: createCurrentProblemsProperties,
-  reloadAction: currentProblemsReloadAction,
+  actionHandler: currentAlarmsActionHandler,
+  createActions: createCurrentAlarmsActions,
+  createProperties: createCurrentAlarmsProperties,
+  reloadAction: currentAlarmsReloadAction,
 
   // set value action, to change a value
-} = createExternal<Fault>(currentProblemsSearchHandler, appState => appState.fault.currentProblems);
+} = createExternal<Fault>(currentAlarmsSearchHandler, appState => appState.fault.currentAlarms);
 
index 2ab1da2..e4a19ae 100644 (file)
  */
 // main state handler
 
+import { IActionHandler } from '../../../../framework/src/flux/action';
 import { combineActionHandler } from '../../../../framework/src/flux/middleware';
-
 // ** do not remove **
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
 import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
-import { IActionHandler } from '../../../../framework/src/flux/action';
 
-import { IFaultNotifications, faultNotificationsHandler } from './notificationsHandler';
-import { ICurrentProblemsState, currentProblemsActionHandler } from './currentProblemsHandler';
-import { IAlarmLogEntriesState, alarmLogEntriesActionHandler } from './alarmLogEntriesHandler';
 import { SetPanelAction } from '../actions/panelChangeActions';
-import { IFaultStatus, faultStatusHandler } from './faultStatusHandler';
-import { stuckAlarmHandler } from './clearStuckAlarmsHandler';
 import { PanelId } from '../models/panelId';
+import { alarmLogEntriesActionHandler, IAlarmLogEntriesState } from './alarmLogEntriesHandler';
+import { currentAlarmsActionHandler, ICurrentAlarmsState } from './currentAlarmsHandler';
+import { faultStatusHandler, IFaultStatus } from './faultStatusHandler';
+import { faultNotificationsHandler, IFaultNotifications } from './notificationsHandler';
 
 export interface IFaultAppStoreState {
-  currentProblems: ICurrentProblemsState;
+  currentAlarms: ICurrentAlarmsState;
   faultNotifications: IFaultNotifications;
   alarmLogEntries: IAlarmLogEntriesState;
   currentOpenPanel: PanelId | null;
@@ -44,7 +43,7 @@ const currentOpenPanelHandler: IActionHandler<PanelId | null> = (state = null, a
     state = action.panelId;
   }
   return state;
-}
+};
 
 declare module '../../../../framework/src/store/applicationStore' {
   interface IApplicationStoreState {
@@ -53,7 +52,7 @@ declare module '../../../../framework/src/store/applicationStore' {
 }
 
 const actionHandlers = {
-  currentProblems: currentProblemsActionHandler,
+  currentAlarms: currentAlarmsActionHandler,
   faultNotifications: faultNotificationsHandler,
   alarmLogEntries: alarmLogEntriesActionHandler,
   currentOpenPanel: currentOpenPanelHandler,
index 6fa95b6..21b033e 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import { IActionHandler } from "../../../../framework/src/flux/action";
-import { SetFaultStatusAction } from "../actions/statusActions";
+import { IActionHandler } from '../../../../framework/src/flux/action';
+
+import { SetFaultStatusAction } from '../actions/statusActions';
 
 export interface IFaultStatus {
-  critical: number,
-  major: number,
-  minor: number,
-  warning: number,
-  isLoadingAlarmStatusChart: boolean,
-  Connected: number,
-  Connecting: number,
-  Disconnected: number,
-  Mounted: number,
-  UnableToConnect: number,
-  Undefined: number,
-  Unmounted: number,
-  total: number,
-  isLoadingConnectionStatusChart: boolean
+  critical: number;
+  major: number;
+  minor: number;
+  warning: number;
+  isLoadingAlarmStatusChart: boolean;
+  Connected: number;
+  Connecting: number;
+  Disconnected: number;
+  Mounted: number;
+  UnableToConnect: number;
+  Undefined: number;
+  Unmounted: number;
+  total: number;
+  isLoadingConnectionStatusChart: boolean;
 }
 
 const faultStatusInit: IFaultStatus = {
@@ -49,7 +50,7 @@ const faultStatusInit: IFaultStatus = {
   Undefined: 0,
   Unmounted: 0,
   total: 0,
-  isLoadingConnectionStatusChart: false
+  isLoadingConnectionStatusChart: false,
 };
 
 export const faultStatusHandler: IActionHandler<IFaultStatus> = (state = faultStatusInit, action) => {
@@ -68,9 +69,9 @@ export const faultStatusHandler: IActionHandler<IFaultStatus> = (state = faultSt
       Undefined: action.UndefinedCount,
       Unmounted: action.UnmountedCount,
       total: action.totalCount,
-      isLoadingConnectionStatusChart: action.isLoadingConnectionStatusChart
-    }
+      isLoadingConnectionStatusChart: action.isLoadingConnectionStatusChart,
+    };
   }
 
   return state;
-}
\ No newline at end of file
+};
\ No newline at end of file
index aa92d2a..3d960bf 100644 (file)
@@ -16,6 +16,7 @@
  * ============LICENSE_END==========================================================================
  */
 import { IActionHandler } from '../../../../framework/src/flux/action';
+
 import { AddFaultNotificationAction, ResetFaultNotificationsAction } from '../actions/notificationActions';
 import { FaultAlarmNotification } from '../models/fault';
 
@@ -26,22 +27,22 @@ export interface IFaultNotifications {
 
 const faultNotoficationsInit: IFaultNotifications = {
   faults: [],
-  since: new Date()
+  since: new Date(),
 };
 
 export const faultNotificationsHandler: IActionHandler<IFaultNotifications> = (state = faultNotoficationsInit, action) => {
   if (action instanceof AddFaultNotificationAction) {
     state = {
       ...state,
-      faults: [...state.faults, action.fault]
+      faults: [...state.faults, action.fault],
     };
-  } else if (action instanceof ResetFaultNotificationsAction){
+  } else if (action instanceof ResetFaultNotificationsAction) {
     state = {
       ...state,
       faults: [],
-      since: new Date()
+      since: new Date(),
     };
   }
 
   return state;
-}
\ No newline at end of file
+};
\ No newline at end of file
index 5f38e5f..c70253e 100644 (file)
@@ -25,7 +25,7 @@ export type Fault = {
   severity: null | 'Warning' | 'Minor' | 'Major' | 'Critical' | 'NonAlarmed';
   type: string;
   sourceType?: string;
-}
+};
 
 export type FaultAlarmNotification = {
   id: string;
@@ -35,63 +35,63 @@ export type FaultAlarmNotification = {
   objectId: string;
   problem: string;
   severity: string;
-}
+};
 
 export type FaultAlarmNotificationWS = {
-  "node-id": string;
-  "data": {
-    "counter": number;
-    "time-stamp": string;
-    "object-id-ref": string;
-    "problem": string;
-    "severity": null | 'Warning' | 'Minor' | 'Major' | 'Critical' | 'NonAlarmed';
+  'node-id': string;
+  'data': {
+    'counter': number;
+    'time-stamp': string;
+    'object-id-ref': string;
+    'problem': string;
+    'severity': null | 'Warning' | 'Minor' | 'Major' | 'Critical' | 'NonAlarmed';
   };
-  "type": {
-    "namespace": string;
-    "revision": string;
-    "type": string;
+  'type': {
+    'namespace': string;
+    'revision': string;
+    'type': string;
   };
-  "event-time": string;
-}
+  'event-time': string;
+};
 
 /**
  * Fault status return type
  */
 export type FaultsReturnType = {
-  criticals: number,
-  majors: number,
-  minors: number,
-  warnings: number,
-  Connected: number,
-  Connecting: number,
-  Disconnected: number,
-  Mounted: number,
-  UnableToConnect: number,
-  Undefined: number,
-  Unmounted: number,
-  total: number
+  criticals: number;
+  majors: number;
+  minors: number;
+  warnings: number;
+  Connected: number;
+  Connecting: number;
+  Disconnected: number;
+  Mounted: number;
+  UnableToConnect: number;
+  Undefined: number;
+  Unmounted: number;
+  total: number;
 };
 
 export type FaultType = {
-  Critical: number,
-  Major: number,
-  Minor: number,
-  Warning: number,
-  Connected: number,
-  Connecting: number,
-  Disconnected: number,
-  Mounted: number,
-  UnableToConnect: number,
-  Undefined: number,
-  Unmounted: number,
-  total: number
+  Critical: number;
+  Major: number;
+  Minor: number;
+  Warning: number;
+  Connected: number;
+  Connecting: number;
+  Disconnected: number;
+  Mounted: number;
+  UnableToConnect: number;
+  Undefined: number;
+  Unmounted: number;
+  total: number;
 };
 
 export type Faults = {
-  faults: FaultsReturnType,
-  'network-element-connections': FaultsReturnType
+  faults: FaultsReturnType;
+  'network-element-connections': FaultsReturnType;
 };
 
 export type DeletedStuckAlarms = {
-    nodenames: string[]
-}
\ No newline at end of file
+  nodenames: string[];
+};
\ No newline at end of file
index 186aa53..daebad0 100644 (file)
@@ -15,4 +15,4 @@
  * the License.
  * ============LICENSE_END==========================================================================
  */
-export type PanelId = null | "CurrentProblem" | "AlarmNotifications" | "AlarmLog";
\ No newline at end of file
+export type PanelId = null | 'CurrentAlarms' | 'AlarmNotifications' | 'AlarmLog';
\ No newline at end of file
index ff901b0..ca1cfc1 100644 (file)
  */
 // app configuration and main entry point for the app
 
+import React from 'react';
+import { Redirect, Route, RouteComponentProps, Switch, withRouter } from 'react-router-dom';
 
-import * as React from "react";
-import { withRouter, RouteComponentProps, Route, Switch, Redirect } from 'react-router-dom';
-
-import connect, { Connect, IDispatcher } from '../../../framework/src/flux/connect';
-
-import { faBell } from '@fortawesome/free-solid-svg-icons';  // select app icon
+import { connect, Connect, IDispatcher } from '../../../framework/src/flux/connect';
 import applicationManager from '../../../framework/src/services/applicationManager';
-import { subscribe, IFormatedMessage } from '../../../framework/src/services/notificationService';
-import { IApplicationStoreState } from "../../../framework/src/store/applicationStore";
-
+import { IFormatedMessage, subscribe } from '../../../framework/src/services/notificationService';
+import { IApplicationStoreState } from '../../../framework/src/store/applicationStore';
+
+import { AddFaultNotificationAction } from './actions/notificationActions';
+import { SetPanelAction } from './actions/panelChangeActions';
+import { refreshFaultStatusAsyncAction, SetFaultStatusAction } from './actions/statusActions';
+import DashboardHome from './components/dashboardHome';
+import { FaultStatus } from './components/faultStatus';
+import { createCurrentAlarmsActions, createCurrentAlarmsProperties, currentAlarmsReloadAction } from './handlers/currentAlarmsHandler';
 import { faultAppRootHandler } from './handlers/faultAppRootHandler';
-import { FaultApplication } from "./views/faultApplication";
-
-import { FaultAlarmNotificationWS } from "./models/fault";
-import { PanelId } from "./models/panelId";
-
-import { SetPanelAction } from "./actions/panelChangeActions";
-import { AddFaultNotificationAction } from "./actions/notificationActions";
-
-import { createCurrentProblemsProperties, createCurrentProblemsActions, currentProblemsReloadAction } from "./handlers/currentProblemsHandler";
-import { FaultStatus } from "./components/faultStatus";
-import { refreshFaultStatusAsyncAction, SetFaultStatusAction } from "./actions/statusActions";
+import { FaultAlarmNotificationWS } from './models/fault';
+import { PanelId } from './models/panelId';
+import { FaultApplication } from './views/faultApplication';
 
-import DashboardHome from "./components/dashboardHome";
+const appIcon = require('./assets/icons/faultAppIcon.svg');  // select app icon
 
 let currentMountId: string | undefined = undefined;
 let currentSeverity: string | undefined = undefined;
 let refreshInterval: ReturnType<typeof window.setInterval> | null = null;
 
 const mapProps = (state: IApplicationStoreState) => ({
-  currentProblemsProperties: createCurrentProblemsProperties(state),
+  currentAlarmsProperties: createCurrentAlarmsProperties(state),
 });
 
-const mapDisp = (dispatcher: IDispatcher) => ({
-  currentProblemsActions: createCurrentProblemsActions(dispatcher.dispatch, true),
+const mapDispatch = (dispatcher: IDispatcher) => ({
+  currentAlarmsActions: createCurrentAlarmsActions(dispatcher.dispatch, true),
   setCurrentPanel: (panelId: PanelId) => dispatcher.dispatch(new SetPanelAction(panelId)),
 });
 
-const FaultApplicationRouteAdapter = connect(mapProps, mapDisp)((props: RouteComponentProps<{ mountId?: string }> & Connect<typeof mapProps, typeof mapDisp>) => {
+const FaultApplicationRouteAdapter = connect(mapProps, mapDispatch)((props: RouteComponentProps<{ mountId?: string }> & Connect<typeof mapProps, typeof mapDispatch>) => {
   if (currentMountId !== props.match.params.mountId) {
     // route parameter has changed
     currentMountId = props.match.params.mountId || undefined;
     // Hint: This timeout is need, since it is not recommended to change the state while rendering is in progress !
     window.setTimeout(() => {
       if (currentMountId) {
-        props.setCurrentPanel("CurrentProblem");
-        props.currentProblemsActions.onFilterChanged("nodeId", currentMountId);
-        if (!props.currentProblemsProperties.showFilter) {
-          props.currentProblemsActions.onToggleFilter(false);
-          props.currentProblemsActions.onRefresh();
-        }
-        else
-          props.currentProblemsActions.onRefresh();
+        props.setCurrentPanel('CurrentAlarms');
+        props.currentAlarmsActions.onFilterChanged('nodeId', currentMountId);
+        if (!props.currentAlarmsProperties.showFilter) {
+          props.currentAlarmsActions.onToggleFilter(false);
+          props.currentAlarmsActions.onRefresh();
+        } else
+          props.currentAlarmsActions.onRefresh();
       }
     });
   }
   return (
     <FaultApplication />
-  )
+  );
 });
 
-const FaulttApplicationAlarmStatusRouteAdapter = connect(mapProps, mapDisp)((props: RouteComponentProps<{ severity?: string }> & Connect<typeof mapProps, typeof mapDisp>) => {
+const FaultApplicationAlarmStatusRouteAdapter = connect(mapProps, mapDispatch)((props: RouteComponentProps<{ severity?: string }> & Connect<typeof mapProps, typeof mapDispatch>) => {
   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();
+        props.setCurrentPanel('CurrentAlarms');
+        props.currentAlarmsActions.onFilterChanged('severity', currentSeverity);
+        if (!props.currentAlarmsProperties.showFilter) {
+          props.currentAlarmsActions.onToggleFilter(false);
+          props.currentAlarmsActions.onRefresh();
+        } else
+          props.currentAlarmsActions.onRefresh();
       }
     });
   }
   return (
     <FaultApplication />
-  )
+  );
 });
 
 const App = withRouter((props: RouteComponentProps) => (
   <Switch>
-    <Route path={`${props.match.path}/alarmStatus/:severity?`} component={FaulttApplicationAlarmStatusRouteAdapter} />
+    <Route path={`${props.match.path}/alarmStatus/:severity?`} component={FaultApplicationAlarmStatusRouteAdapter} />
     <Route path={`${props.match.path}/:mountId?`} component={FaultApplicationRouteAdapter} />
     <Redirect to={`${props.match.path}`} />
   </Switch>
@@ -110,54 +103,48 @@ const App = withRouter((props: RouteComponentProps) => (
 
 export function register() {
   const applicationApi = applicationManager.registerApplication({
-    name: "fault",
-    icon: faBell,
+    name: 'fault',
+    icon: appIcon,
     rootComponent: App,
     rootActionHandler: faultAppRootHandler,
     statusBarElement: FaultStatus,
     dashbaordElement: DashboardHome,
-    menuEntry: "Fault"
+    menuEntry: 'Fault',
   });
 
   let counter = 0;
   // subscribe to the websocket notifications
-  subscribe<FaultAlarmNotificationWS & IFormatedMessage>("problem-notification", (fault => {
+  subscribe<FaultAlarmNotificationWS & IFormatedMessage>('problem-notification', (fault => {
     const store = applicationApi && applicationApi.applicationStore;
     if (fault && store) {
 
       store.dispatch(new AddFaultNotificationAction({
         id: String(counter++),
-        nodeName: fault["node-id"],
+        nodeName: fault['node-id'],
         counter: +fault.data.counter,
-        objectId: fault.data["object-id-ref"],
+        objectId: fault.data['object-id-ref'],
         problem: fault.data.problem,
         severity: fault.data.severity || '',
-        timeStamp: fault.data["time-stamp"],
+        timeStamp: fault.data['time-stamp'],
       }));
     }
   }));
 
   applicationApi.applicationStoreInitialized.then(store => {
-    store.dispatch(currentProblemsReloadAction);
+    store.dispatch(currentAlarmsReloadAction);
   });
 
   applicationApi.applicationStoreInitialized.then(store => {
     store.dispatch(refreshFaultStatusAsyncAction);
   });
 
-  applicationApi.loginEvent.addHandler(e=>{
-    refreshInterval = startRefreshInterval() as any;
-  })
-
-  applicationApi.logoutEvent.addHandler(e=>{
+  applicationApi.logoutEvent.addHandler(()=>{
 
     applicationApi.applicationStoreInitialized.then(store => {
       store.dispatch(new SetFaultStatusAction(0, 0, 0, 0, false, 0, 0, 0, 0, 0, 0, 0, 0, false));
       clearInterval(refreshInterval!);
     });
-  })
-  
+  });
 
   function startRefreshInterval()  {
     const refreshFaultStatus = window.setInterval(() => {
@@ -170,4 +157,7 @@ export function register() {
     return refreshFaultStatus;
   }
 
+  applicationApi.loginEvent.addHandler(()=>{
+    refreshInterval = startRefreshInterval() as any;
+  });
 }
index 880ed77..0c7a215 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import { requestRest } from "../../../../framework/src/services/restService";
-import { Result, SingeResult } from "../../../../framework/src/models/elasticSearch";
-import { FaultType, Faults, DeletedStuckAlarms } from "../models/fault";
+import { Result } from '../../../../framework/src/models/elasticSearch';
+import { requestRest } from '../../../../framework/src/services/restService';
+
+import { Faults, FaultType } from '../models/fault';
 
 
 export const getFaultStateFromDatabase = async (): Promise<FaultType | null> => {
   const path = 'rests/operations/data-provider:read-status';
-  const result = await requestRest<Result<Faults>>(path, { method: "POST" });
+  const result = await requestRest<Result<Faults>>(path, { method: 'POST' });
 
   let faultType: FaultType = {
     Critical: 0,
@@ -36,34 +37,33 @@ export const getFaultStateFromDatabase = async (): Promise<FaultType | null> =>
     UnableToConnect: 0,
     Undefined: 0,
     Unmounted: 0,
-    total: 0
-  }
+    total: 0,
+  };
   let faults: Faults[] | null = null;
 
-  if (result && result["data-provider:output"] && result["data-provider:output"].data) {
-    faults = result["data-provider:output"].data;
+  if (result && result['data-provider:output'] && result['data-provider:output'].data) {
+    faults = result['data-provider:output'].data;
     faultType = {
       Critical: faults[0].faults.criticals,
       Major: faults[0].faults.majors,
       Minor: faults[0].faults.minors,
       Warning: faults[0].faults.warnings,
-      Connected: faults[0]["network-element-connections"].Connected,
-      Connecting: faults[0]["network-element-connections"].Connecting,
-      Disconnected: faults[0]["network-element-connections"].Disconnected,
-      Mounted: faults[0]["network-element-connections"].Mounted,
-      UnableToConnect: faults[0]["network-element-connections"].UnableToConnect,
-      Undefined: faults[0]["network-element-connections"].Undefined,
-      Unmounted: faults[0]["network-element-connections"].Unmounted,
-      total: faults[0]["network-element-connections"].total,
-    }
+      Connected: faults[0]['network-element-connections'].Connected,
+      Connecting: faults[0]['network-element-connections'].Connecting,
+      Disconnected: faults[0]['network-element-connections'].Disconnected,
+      Mounted: faults[0]['network-element-connections'].Mounted,
+      UnableToConnect: faults[0]['network-element-connections'].UnableToConnect,
+      Undefined: faults[0]['network-element-connections'].Undefined,
+      Unmounted: faults[0]['network-element-connections'].Unmounted,
+      total: faults[0]['network-element-connections'].total,
+    };
   }
 
   return faultType;
-}
+};
 
 export const clearStuckAlarms = async (nodeNames: string[]) => {
-  const path = 'rests/operations/devicemanager:clear-current-fault-by-nodename'
-  const result = await requestRest<any>(path, { method: 'Post', body: JSON.stringify({ input: { nodenames: nodeNames } }) })
+  const path = 'rests/operations/devicemanager:clear-current-fault-by-nodename';
+  const result = await requestRest<any>(path, { method: 'Post', body: JSON.stringify({ input: { nodenames: nodeNames } }) });
   return result;
-
-}
\ No newline at end of file
+};
\ No newline at end of file
index b9cd5e4..b9f115e 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
+import React from 'react';
+import { RouteComponentProps, withRouter } from 'react-router-dom';
 
-import { withRouter, RouteComponentProps } from 'react-router-dom';
-
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
-
-import { MaterialTable, ColumnType, MaterialTableCtorType } from '../../../../framework/src/components/material-table';
-import { Panel } from '../../../../framework/src/components/material-ui';
+import Refresh from '@mui/icons-material/Refresh';
+import Sync from '@mui/icons-material/Sync';
+import { AppBar, Tab, Tabs } from '@mui/material';
 
+import { ColumnType, MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table';
+import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect';
 import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
-import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect';
 
-import { Fault, FaultAlarmNotification } from '../models/fault';
-import { PanelId } from '../models/panelId';
-
-import { createCurrentProblemsProperties, createCurrentProblemsActions, currentProblemsReloadAction } from '../handlers/currentProblemsHandler';
-import { createAlarmLogEntriesProperties, createAlarmLogEntriesActions, alarmLogEntriesReloadAction } from '../handlers/alarmLogEntriesHandler';
 import { setPanelAction } from '../actions/panelChangeActions';
-import { Tooltip, IconButton, AppBar, Tabs, Tab } from '@mui/material';
-import Sync from '@mui/icons-material/Sync';
-import Refresh from '@mui/icons-material/Refresh';
-
 import ClearStuckAlarmsDialog, { ClearStuckAlarmsDialogMode } from '../components/clearStuckAlarmsDialog';
 import RefreshAlarmLogDialog, { RefreshAlarmLogDialogMode } from '../components/refreshAlarmLogDialog';
-import RefreshCurrentProblemsDialog, { RefreshCurrentProblemsDialogMode } from '../components/refreshCurrentProblemsDialog';
+import RefreshCurrentAlarmsDialog, { RefreshCurrentAlarmsDialogMode } from '../components/refreshCurrentAlarmsDialog';
+import { alarmLogEntriesReloadAction, createAlarmLogEntriesActions, createAlarmLogEntriesProperties } from '../handlers/alarmLogEntriesHandler';
+import { createCurrentAlarmsActions, createCurrentAlarmsProperties, currentAlarmsReloadAction } from '../handlers/currentAlarmsHandler';
+import { Fault, FaultAlarmNotification } from '../models/fault';
+import { PanelId } from '../models/panelId';
 
 const mapProps = (state: IApplicationStoreState) => ({
   panelId: state.fault.currentOpenPanel,
-  currentProblemsProperties: createCurrentProblemsProperties(state),
+  currentAlarmsProperties: createCurrentAlarmsProperties(state),
   faultNotifications: state.fault.faultNotifications,
-  alarmLogEntriesProperties: createAlarmLogEntriesProperties(state)
+  alarmLogEntriesProperties: createAlarmLogEntriesProperties(state),
 });
 
 const mapDisp = (dispatcher: IDispatcher) => ({
-  currentProblemsActions: createCurrentProblemsActions(dispatcher.dispatch),
+  currentAlarmsActions: createCurrentAlarmsActions(dispatcher.dispatch),
   alarmLogEntriesActions: createAlarmLogEntriesActions(dispatcher.dispatch),
-  reloadCurrentProblems: () => dispatcher.dispatch(currentProblemsReloadAction),
+  reloadCurrentAlarms: () => dispatcher.dispatch(currentAlarmsReloadAction),
   reloadAlarmLogEntries: () => dispatcher.dispatch(alarmLogEntriesReloadAction),
   switchActivePanel: (panelId: PanelId) => {
     dispatcher.dispatch(setPanelAction(panelId));
@@ -62,63 +55,59 @@ const mapDisp = (dispatcher: IDispatcher) => ({
 type FaultApplicationComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp>;
 
 type FaultApplicationState = {
-  clearAlarmDialogMode: ClearStuckAlarmsDialogMode,
-  stuckAlarms: string[],
-  refreshAlarmLogEditorMode: RefreshAlarmLogDialogMode,
-  refreshCurrentProblemsEditorMode: RefreshCurrentProblemsDialogMode
-}
+  clearAlarmDialogMode: ClearStuckAlarmsDialogMode;
+  stuckAlarms: string[];
+  refreshAlarmLogEditorMode: RefreshAlarmLogDialogMode;
+  refreshCurrentAlarmsEditorMode: RefreshCurrentAlarmsDialogMode;
+};
 
 
 const FaultTable = MaterialTable as MaterialTableCtorType<Fault>;
 const FaultAlarmNotificationTable = MaterialTable as MaterialTableCtorType<FaultAlarmNotification>;
 
-let currentProblemsInitalSorted = false;
+let currentAlarmsInitalSorted = false;
 let alarmLogInitialSorted = false;
 
-class FaultApplicationComponent extends React.Component<FaultApplicationComponentProps, FaultApplicationState>{
-
-  /**
-   *
-   */
+class FaultApplicationComponent extends React.Component<FaultApplicationComponentProps, FaultApplicationState> {
   constructor(props: FaultApplicationComponentProps) {
     super(props);
     this.state = {
       clearAlarmDialogMode: ClearStuckAlarmsDialogMode.None,
       stuckAlarms: [],
       refreshAlarmLogEditorMode: RefreshAlarmLogDialogMode.None,
-      refreshCurrentProblemsEditorMode: RefreshCurrentProblemsDialogMode.None
-    }
+      refreshCurrentAlarmsEditorMode: RefreshCurrentAlarmsDialogMode.None,
+    };
   }
 
   onDialogClose = () => {
-    this.setState({ clearAlarmDialogMode: ClearStuckAlarmsDialogMode.None, stuckAlarms: [] })
-  }
+    this.setState({ clearAlarmDialogMode: ClearStuckAlarmsDialogMode.None, stuckAlarms: [] });
+  };
 
   onDialogOpen = () => {
-    const stuckAlarms = [...new Set(this.props.currentProblemsProperties.rows.map(item => item.nodeId))];
-    this.setState({ clearAlarmDialogMode: ClearStuckAlarmsDialogMode.Show, stuckAlarms: stuckAlarms })
-  }
+    const stuckAlarms = [...new Set(this.props.currentAlarmsProperties.rows.map(item => item.nodeId))];
+    this.setState({ clearAlarmDialogMode: ClearStuckAlarmsDialogMode.Show, stuckAlarms: stuckAlarms });
+  };
 
   private onHandleTabChange = (event: React.SyntheticEvent, newValue: PanelId) => {
     this.onToggleTabs(newValue);
-  }
+  };
 
   private onToggleTabs = (panelId: PanelId) => {
     const nextActivePanel = panelId;
     this.props.switchActivePanel(nextActivePanel);
     switch (nextActivePanel) {
-      case 'CurrentProblem':
-        if (!currentProblemsInitalSorted) {
-          currentProblemsInitalSorted = true;
-          this.props.currentProblemsActions.onHandleExplicitRequestSort("timestamp", "desc");
+      case 'CurrentAlarms':
+        if (!currentAlarmsInitalSorted) {
+          currentAlarmsInitalSorted = true;
+          this.props.currentAlarmsActions.onHandleExplicitRequestSort('timestamp', 'desc');
         } else {
-          this.props.reloadCurrentProblems();
+          this.props.reloadCurrentAlarms();
         }
         break;
       case 'AlarmLog':
         if (!alarmLogInitialSorted) {
           alarmLogInitialSorted = true;
-          this.props.alarmLogEntriesActions.onHandleExplicitRequestSort("timestamp", "desc");
+          this.props.alarmLogEntriesActions.onHandleExplicitRequestSort('timestamp', 'desc');
         } else {
           this.props.reloadAlarmLogEntries();
         }
@@ -136,27 +125,27 @@ class FaultApplicationComponent extends React.Component<FaultApplicationComponen
   render(): JSX.Element {
 
     const clearAlarmsAction = {
-      icon: Sync, tooltip: 'Clear stuck alarms', ariaLabel:'clear-stuck-alarms', onClick: this.onDialogOpen
+      icon: Sync, tooltip: 'Clear stuck alarms', ariaLabel:'clear-stuck-alarms', onClick: this.onDialogOpen,
     };
 
-    const refreshCurrentProblemsAction = {
-      icon: Refresh, tooltip: 'Refresh Current Problems List', ariaLabel:'refresh', onClick: () => {
+    const refreshCurrentAlarmsAction = {
+      icon: Refresh, tooltip: 'Refresh Current Alarms List', ariaLabel:'refresh', onClick: () => {
         this.setState({
-          refreshCurrentProblemsEditorMode: RefreshCurrentProblemsDialogMode.RefreshCurrentProblemsTable
+          refreshCurrentAlarmsEditorMode: RefreshCurrentAlarmsDialogMode.RefreshCurrentAlarmsTable,
         });
-      }
+      },
     };
 
     const refreshAlarmLogAction = {
       icon: Refresh, tooltip: 'Refresh Alarm log table', ariaLabel:'refresh', onClick: () => {
         this.setState({
-          refreshAlarmLogEditorMode: RefreshAlarmLogDialogMode.RefreshAlarmLogTable
+          refreshAlarmLogEditorMode: RefreshAlarmLogDialogMode.RefreshAlarmLogTable,
         });
-      }
+      },
     };
 
-    const areFaultsAvailable = this.props.currentProblemsProperties.rows && this.props.currentProblemsProperties.rows.length > 0
-    const customActions = areFaultsAvailable ? [clearAlarmsAction, refreshCurrentProblemsAction] : [refreshCurrentProblemsAction];
+    const areFaultsAvailable = this.props.currentAlarmsProperties.rows && this.props.currentAlarmsProperties.rows.length > 0;
+    const customActions = areFaultsAvailable ? [clearAlarmsAction, refreshCurrentAlarmsAction] : [refreshCurrentAlarmsAction];
 
     const { panelId: activePanelId } = this.props;
 
@@ -164,26 +153,26 @@ class FaultApplicationComponent extends React.Component<FaultApplicationComponen
       <>
         <AppBar enableColorOnDark position="static" >
           <Tabs indicatorColor="secondary" textColor="inherit" value={activePanelId} onChange={this.onHandleTabChange} aria-label="fault-tabs">
-            <Tab aria-label="current-problem-list-tab" label="Current Problem List" value="CurrentProblem" />
+            <Tab aria-label="current-alarms-list-tab" label="Current Alarms" value="CurrentAlarms" />
             <Tab aria-label="alarm-notifications-list-tab" label={`Alarm Notifications (${this.props.faultNotifications.faults.length})`} value="AlarmNotifications" />
             <Tab aria-label="alarm-log-tab" label="Alarm Log" value="AlarmLog" />
           </Tabs>
         </AppBar>
         {
-          activePanelId === 'CurrentProblem' &&
+          activePanelId === 'CurrentAlarms' &&
           <>
-            <FaultTable stickyHeader tableId="current-problems-table" idProperty="id" customActionButtons={customActions} columns={[
+            <FaultTable stickyHeader tableId="current-alarms-table" idProperty="id" customActionButtons={customActions} columns={[
               // { property: "icon", title: "", type: ColumnType.custom, customControl: this.renderIcon },
-              { property: "severity", title: "Severity", type: ColumnType.text, width: "140px" },
-              { property: "timestamp", type: ColumnType.text, title: "Timestamp" },
-              { property: "nodeId", title: "Node Name", type: ColumnType.text },
-              { property: "counter", title: "Count", type: ColumnType.numeric, width: "100px" },
-              { property: "objectId", title: "Object Id", type: ColumnType.text },
-              { property: "problem", title: "Alarm Type", type: ColumnType.text },
-            ]} {...this.props.currentProblemsProperties} {...this.props.currentProblemsActions} />
-            <RefreshCurrentProblemsDialog
-              mode={this.state.refreshCurrentProblemsEditorMode}
-              onClose={this.onCloseRefreshCurrentProblemsDialog}
+              { property: 'severity', title: 'Severity', type: ColumnType.text, width: '140px' },
+              { property: 'timestamp', type: ColumnType.text, title: 'Timestamp' },
+              { property: 'nodeId', title: 'Node Name', type: ColumnType.text },
+              { property: 'counter', title: 'Count', type: ColumnType.numeric, width: '100px' },
+              { property: 'objectId', title: 'Object Id', type: ColumnType.text },
+              { property: 'problem', title: 'Alarm Type', type: ColumnType.text },
+            ]} {...this.props.currentAlarmsProperties} {...this.props.currentAlarmsActions} />
+            <RefreshCurrentAlarmsDialog
+              mode={this.state.refreshCurrentAlarmsEditorMode}
+              onClose={this.onCloseRefreshCurrentAlarmsDialog}
             />
           </>
         }
@@ -191,12 +180,12 @@ class FaultApplicationComponent extends React.Component<FaultApplicationComponen
 
           <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: "severity", title: "Severity", width: "140px" },
-            { property: "timeStamp", title: "Timestamp" },
-            { property: "nodeName", title: "Node Name" },
-            { property: "counter", title: "Count", width: "100px", type: ColumnType.numeric },
-            { property: "objectId", title: "Object Id" },
-            { property: "problem", title: "Alarm Type" },
+            { property: 'severity', title: 'Severity', width: '140px', type: ColumnType.text },
+            { property: 'timeStamp', title: 'Timestamp', type: ColumnType.text },
+            { property: 'nodeName', title: 'Node Name', type: ColumnType.text },
+            { property: 'counter', title: 'Count', width: '100px', type: ColumnType.numeric },
+            { property: 'objectId', title: 'Object Id', type: ColumnType.text },
+            { property: 'problem', title: 'Alarm Type', type: ColumnType.text },
           ]} />
         }
 
@@ -205,13 +194,13 @@ class FaultApplicationComponent extends React.Component<FaultApplicationComponen
             <FaultTable stickyHeader idProperty={'id'} tableId="alarm-log-table" customActionButtons={[refreshAlarmLogAction]}
               columns={[
                 // { property: "icon", title: "", type: ColumnType.custom, customControl: this.renderIcon },
-                { property: "severity", title: "Severity", width: "140px" },
-                { property: "timestamp", title: "Timestamp" },
-                { property: "nodeId", title: "Node Name" },
-                { property: "counter", title: "Count", type: ColumnType.numeric, width: "100px" },
-                { property: "objectId", title: "Object Id" },
-                { property: "problem", title: "Alarm Type" },
-                { property: "sourceType", title: "Source", width: "140px" },
+                { property: 'severity', title: 'Severity', width: '140px' },
+                { property: 'timestamp', title: 'Timestamp' },
+                { property: 'nodeId', title: 'Node Name' },
+                { property: 'counter', title: 'Count', type: ColumnType.numeric, width: '100px' },
+                { property: 'objectId', title: 'Object Id' },
+                { property: 'problem', title: 'Alarm Type' },
+                { property: 'sourceType', title: 'Source', width: '140px' },
               ]} {...this.props.alarmLogEntriesProperties} {...this.props.alarmLogEntriesActions} />
             <RefreshAlarmLogDialog
               mode={this.state.refreshAlarmLogEditorMode}
@@ -224,34 +213,34 @@ class FaultApplicationComponent extends React.Component<FaultApplicationComponen
           this.state.clearAlarmDialogMode !== ClearStuckAlarmsDialogMode.None && <ClearStuckAlarmsDialog mode={this.state.clearAlarmDialogMode} numberDevices={this.state.stuckAlarms.length} stuckAlarms={this.state.stuckAlarms} onClose={this.onDialogClose} />
         }
       </>
-    )
-
-  };
+    );
+  }
 
   public componentDidMount() {
     if (this.props.panelId === null) { //set default tab if none is set
-      this.onToggleTabs("CurrentProblem");
-    }else{
+      this.onToggleTabs('CurrentAlarms');
+    } else {
       this.onToggleTabs(this.props.panelId);
     }
   }
 
-  private renderIcon = (props: { rowData: Fault | FaultAlarmNotification }) => {
-    return (
-      <FontAwesomeIcon icon={faExclamationTriangle} />
-    );
-  };
+  // private renderIcon = (props: { rowData: Fault | FaultAlarmNotification }) => {
+  //   return (
+  //     <FontAwesomeIcon icon={faExclamationTriangle} />
+  //   );
+  // };
 
   private onCloseRefreshAlarmLogDialog = () => {
     this.setState({
-      refreshAlarmLogEditorMode: RefreshAlarmLogDialogMode.None
+      refreshAlarmLogEditorMode: RefreshAlarmLogDialogMode.None,
     });
-  }
-  private onCloseRefreshCurrentProblemsDialog = () => {
+  };
+
+  private onCloseRefreshCurrentAlarmsDialog = () => {
     this.setState({
-      refreshCurrentProblemsEditorMode: RefreshCurrentProblemsDialogMode.None
+      refreshCurrentAlarmsEditorMode: RefreshCurrentAlarmsDialogMode.None,
     });
-  }
+  };
 }
 
 export const FaultApplication = withRouter(connect(mapProps, mapDisp)(FaultApplicationComponent));
index a66b5d8..ca65092 100644 (file)
@@ -4,7 +4,7 @@
     "outDir": "./dist",
     "sourceMap": true,
     "forceConsistentCasingInFileNames": true,
-    "allowSyntheticDefaultImports": false,
+    "allowSyntheticDefaultImports": true,
     "allowUnreachableCode": false,
     "allowUnusedLabels": false,
     "noFallthroughCasesInSwitch": true,
index d34d31c..bc26de1 100644 (file)
@@ -57,6 +57,16 @@ module.exports = (env) => {
         use: [{
           loader: "babel-loader"
         }]
+      },{
+        //don't minify images
+        test: /\.(png|gif|jpg|svg)$/,
+        use: [{
+          loader: 'url-loader',
+          options: {
+            limit: 10,
+            name: './images/[name].[ext]'
+          }
+        }]
       }]
     },
     optimization: {
@@ -125,27 +135,27 @@ module.exports = (env) => {
       },
        proxy: {
         "/oauth2/": {
-          target: "http://sdnr:8181",
+          target: "http://sdnc-web:8080",
           secure: false
         },
         "/database/": {
-          target: "http://sdnr:8181",
+          target: "http://sdnc-web:8080",
           secure: false
         },
         "/restconf/": {
-          target: "http://sdnr:8181",
+          target: "http://sdnc-web:8080",
           secure: false
         },
         "/rests/": {
-          target: "http://sdnr:8181",
+          target: "http://sdnc-web:8080",
           secure: false
         },
         "/help/": {
-          target: "http://sdnr:8181",
+          target: "http://sdnc-web:8080",
           secure: false
         },
         "/websocket": {
-          target: "http://sdnr:8181",
+          target: "http://sdnc-web:8080",
           ws: true,
           changeOrigin: true,
           secure: false
index 9da0309..07ac091 100644 (file)
@@ -19,6 +19,7 @@
   ~ ============LICENSE_END=======================================================
   ~
   -->
+
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
                         <!-- optional: default phase is "generate-resources" -->
                         <phase>initialize</phase>
                         <configuration>
-                            <nodeVersion>v12.13.0</nodeVersion>
+                            <nodeVersion>v12.22.0</nodeVersion>
                             <yarnVersion>v1.22.10</yarnVersion>
                         </configuration>
                     </execution>
diff --git a/sdnr/wt/odlux/apps/helpApp/src/assets/icons/helpAppIcon.svg b/sdnr/wt/odlux/apps/helpApp/src/assets/icons/helpAppIcon.svg
new file mode 100644 (file)
index 0000000..298eaa1
--- /dev/null
@@ -0,0 +1,27 @@
+<!-- highstreet technologies GmbH colour scheme 
+       Grey    #565656
+       LBlue #36A9E1
+       DBlue #246DA2
+       Green #003F2C / #006C4B
+       Yellw #C8D400
+       Red     #D81036
+-->
+
+<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="310 250 400 400">
+
+<g transform="translate(0,1024) scale(0.1,-0.1)">
+
+<path fill="#565656" d="M4926 7634 c-126 -17 -209 -38 -318 -79 -79 -31 -195 -89 -208 -104
+-10 -12 -69 -51 -77 -51 -4 0 -42 -28 -83 -63 -227 -190 -375 -475 -375 -722
+0 -81 3 -95 30 -143 111 -201 365 -252 514 -103 46 46 88 124 121 226 28 87
+109 255 153 315 67 95 172 168 275 192 86 20 268 21 346 2 113 -28 152 -50
+240 -137 64 -63 88 -95 104 -137 49 -125 52 -225 12 -332 -38 -102 -132 -209
+-360 -409 -153 -134 -329 -309 -375 -374 -97 -136 -148 -274 -166 -448 -19
+-192 12 -305 104 -379 64 -50 141 -72 228 -65 82 7 125 24 177 71 49 45 73
+100 105 241 59 258 63 263 528 687 218 198 295 284 374 419 134 230 138 543 9
+803 -101 202 -252 349 -474 461 -246 124 -573 172 -884 129z"/>
+
+<path fill="#36A9E1" d="M 5098 4587 C 4582 4587 4582 3845 5098 3845 C 5614 3847 5614 4585 5098 4587 Z"/>
+
+</g>
+</svg>
index fd4cd3f..985b404 100644 (file)
@@ -15,7 +15,8 @@
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
+import React from 'react';
+import { withRouter, RouteComponentProps } from 'react-router';
 
 import { Theme } from '@mui/material/styles';
 import { WithStyles } from '@mui/styles';
@@ -23,13 +24,12 @@ import withStyles from '@mui/styles/withStyles';
 import createStyles from '@mui/styles/createStyles';
 import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';  // select app icon
 
-import connect, { Connect } from '../../../../framework/src/flux/connect';
-import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
-
 import Typography from '@mui/material/Typography';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons';
-import { withRouter, RouteComponentProps } from 'react-router';
+
+import { connect, Connect } from '../../../../framework/src/flux/connect';
+import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
 
 const styles = (theme: Theme) => createStyles({
   icon: {
index cc6a984..29e0795 100644 (file)
 // main state handler
 
 import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
-import { TocTreeNode } from 'models/tocNode';
 import { IActionHandler } from '../../../../framework/src/flux/action';
+
 import { LoadTocAction, TocLoadedAction, LoadDocumentAction, DocumentLoadedAction } from '../actions/helpActions';
+import { TocTreeNode } from '../models/tocNode';
 
 export interface IHelpAppStoreState {
   busy: boolean;
index 50a264b..5d860e5 100644 (file)
  */
 // app configuration and main entry point for the app
 
-import * as React from "react";
+import React from "react";
 import { withRouter, RouteComponentProps, Route, Switch, Redirect } from 'react-router-dom';
 
-import { faFirstAid } from '@fortawesome/free-solid-svg-icons';  // select app icon
-
 import applicationManager from '../../../framework/src/services/applicationManager';
 import { IApplicationStoreState } from "../../../framework/src/store/applicationStore";
-import connect, { Connect, IDispatcher } from '../../../framework/src/flux/connect';
+import { connect, Connect, IDispatcher } from '../../../framework/src/flux/connect';
 
 import { requestTocAsyncAction, requestDocumentAsyncActionCreator } from "./actions/helpActions";
 import { helpAppRootHandler } from './handlers/helpAppRootHandler';
@@ -35,11 +33,13 @@ import { HelpStatus } from "./components/helpStatus";
 import '!style-loader!css-loader!highlight.js/styles/default.css';
 import HelpTocApp from "./views/helpTocApp";
 
+const appIcon = require('./assets/icons/helpAppIcon.svg');  // select app icon
+
 const mapProps = (state: IApplicationStoreState) => ({
 
 });
 
-const mapDisp = (dispatcher: IDispatcher) => ({
+const mapDispatch = (dispatcher: IDispatcher) => ({
   requestDocument: (path: string) => {
     dispatcher.dispatch(requestDocumentAsyncActionCreator(path));
   }
@@ -47,7 +47,7 @@ const mapDisp = (dispatcher: IDispatcher) => ({
 
 let currentHelpPath: string | undefined = undefined;
 
-const HelpApplicationRouteAdapter = connect(mapProps, mapDisp)((props: RouteComponentProps<{ '0'?: string }> & Connect<typeof mapProps, typeof mapDisp>) => {
+const HelpApplicationRouteAdapter = connect(mapProps, mapDispatch)((props: RouteComponentProps<{ '0'?: string }> & Connect<typeof mapProps, typeof mapDispatch>) => {
 
   if (currentHelpPath !== props.match.params["0"]) {
     // route parameter has changed
@@ -76,7 +76,7 @@ const App = withRouter((props: RouteComponentProps) => (
 export async function register() {
   const applicationApi = applicationManager.registerApplication({
     name: "help",
-    icon: faFirstAid,
+    icon: appIcon,
     rootComponent: App,
     rootActionHandler: helpAppRootHandler,
     statusBarElement: HelpStatus,
@@ -84,7 +84,7 @@ export async function register() {
     //subMenuEntry: SubMenuEntry 
   });
 
-  // start the initial toc request after the application store is initalized
+  // start the initial toc request after the application store is initialized
   const store = await applicationApi.applicationStoreInitialized;
   store.dispatch(requestTocAsyncAction);
 
index eab44b4..5940517 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
+import React from 'react';
 import * as marked from 'marked';
 
 import { resolvePath } from '../utilities/path';
 
 import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
-import connect, { Connect } from '../../../../framework/src/flux/connect';
+import { connect, Connect } from '../../../../framework/src/flux/connect';
 
 import { Markdown } from "../components/markdown";
 
index e1de4d0..6a6a891 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
-import connect, { Connect, IDispatcher } from "../../../../framework/src/flux/connect";
+import React from 'react'
+import {connect, Connect, IDispatcher } from "../../../../framework/src/flux/connect";
 
 import { NavigateToApplication } from "../../../../framework/src/actions/navigationActions";
-import * as React from 'react'
 import { FunctionComponent } from "react";
 import { IApplicationStoreState } from "../../../../framework/src/store/applicationStore";
 import TocEntry from "../components/tocEntry";
index a66b5d8..ca65092 100644 (file)
@@ -4,7 +4,7 @@
     "outDir": "./dist",
     "sourceMap": true,
     "forceConsistentCasingInFileNames": true,
-    "allowSyntheticDefaultImports": false,
+    "allowSyntheticDefaultImports": true,
     "allowUnreachableCode": false,
     "allowUnusedLabels": false,
     "noFallthroughCasesInSwitch": true,
index b069c2a..a48f7b9 100644 (file)
@@ -74,6 +74,16 @@ module.exports = (env) => {
             plugins: () => [autoprefixer]
           }
         }]
+      }, {
+        //don't minify images
+        test: /\.(png|gif|jpg|svg)$/,
+        use: [{
+          loader: 'url-loader',
+          options: {
+            limit: 10,
+            name: './images/[name].[ext]'
+          }
+        }]
       }]
     },
 
index e39e267..c6ec595 100644 (file)
@@ -19,6 +19,7 @@
   ~ ============LICENSE_END=======================================================
   ~
   -->
+
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
                         <!-- optional: default phase is "generate-resources" -->
                         <phase>initialize</phase>
                         <configuration>
-                            <nodeVersion>v12.13.0</nodeVersion>
+                            <nodeVersion>v12.22.0</nodeVersion>
                             <yarnVersion>v1.22.10</yarnVersion>
                         </configuration>
                     </execution>
diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/actions/inventoryDeviceListActions.ts b/sdnr/wt/odlux/apps/inventoryApp/src/actions/inventoryDeviceListActions.ts
new file mode 100644 (file)
index 0000000..710959a
--- /dev/null
@@ -0,0 +1,59 @@
+/**
+ * ============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';
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
+
+import { InventoryDeviceListType } from '../models/inventoryDeviceListType';
+import { inventoryService } from '../services/inventoryService';
+
+/** 
+ * Represents the base action. 
+ */
+export class BaseAction extends Action { }
+
+/** 
+ * Represents an action causing the store to load all nodes. 
+ */
+export class LoadAllInventoryDeviceListAction extends BaseAction { }
+
+/** 
+ * Represents an action causing the store to update all nodes. 
+ */
+export class AllInventoryDeviceListLoadedAction extends BaseAction {
+  /**
+   * Initialize this instance.
+   * 
+   * @param inventoryDeviceList All the distinct nodes from the Inventory  database.
+   */
+  constructor(public inventoryDeviceList: InventoryDeviceListType[] | null, public error?: string) {
+    super();
+  }
+}
+
+/** 
+ * Represents an asynchronous thunk  action to load all nodes. 
+ */
+export const loadAllInventoryDeviceListAsync = async (dispatch: Dispatch) => {
+  dispatch(new LoadAllInventoryDeviceListAction());
+  const inventoryDeviceList: InventoryDeviceListType[] = (await inventoryService.getInventoryDeviceList().then(ne =>
+    (ne))) || [];
+  return inventoryDeviceList && dispatch(new AllInventoryDeviceListLoadedAction(inventoryDeviceList));
+};
+
index c09b669..2c6a0ed 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
+import { AddErrorInfoAction } from '../../../../framework/src/actions/errorActions';
+import { NavigateToApplication } from '../../../../framework/src/actions/navigationActions';
 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 { InventoryTreeNode, InventoryType, 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.
@@ -38,7 +37,7 @@ export class SetBusyAction extends BaseAction {
 }
 
 export class SetSearchTextAction extends BaseAction {
-  constructor(public searchTerm: string = "") {
+  constructor(public searchTerm: string = '') {
     super();
 
   }
@@ -65,40 +64,38 @@ export class UpdateExpandedNodesAction extends BaseAction {
   }
 }
 
-export const setSearchTermAction = (searchTerm: string) => (dispatch: Dispatch, getState: () => IApplicationStoreState) =>{
+export const setSearchTermAction = (searchTerm: string) => (dispatch: Dispatch) =>{
   dispatch(new SetSearchTextAction(searchTerm));
-}
+};
 
 
-export const updateInventoryTreeAsyncAction = (mountId: string, searchTerm?: string) => async (dispatch: Dispatch, getState: () => IApplicationStoreState) => {
+export const updateInventoryTreeAsyncAction = (mountId: string, searchTerm?: string) => async (dispatch: Dispatch) => {
   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"));
+      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 {
+    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) => {
+export const selectInventoryNodeAsyncAction = (nodeId: string) => async (dispatch: Dispatch) => {
   dispatch(new SetBusyAction(true));
   try {
     const result = await inventoryService.getInventoryEntry(nodeId);
-    if (!result) throw new Error("Could not load inventory tree from server.");
+    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 {
+    throw new Error('Could not load inventory tree from server.');
+  } finally {
     dispatch(new SetBusyAction(false));
   }
 };
index 10fde8c..d666082 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
-import { Action } from "../../../../framework/src/flux/action";
-import { PanelId } from "models/panelId";
+import { Action } from '../../../../framework/src/flux/action';
 
+import { PanelId } from '../models/panelId';
 
 export class SetPanelAction extends Action {
-    constructor(public panelId: PanelId) {
-      super();
-    }
+  constructor(public panelId: PanelId) {
+    super();
   }
+}
 
 export const setPanelAction = (panelId: PanelId) => {
-    return new SetPanelAction(panelId);
-  }
\ No newline at end of file
+  return new SetPanelAction(panelId);
+};
\ No newline at end of file
diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/assets/icons/inventoryAppIcon.svg b/sdnr/wt/odlux/apps/inventoryApp/src/assets/icons/inventoryAppIcon.svg
new file mode 100644 (file)
index 0000000..507a835
--- /dev/null
@@ -0,0 +1,23 @@
+<!-- highstreet technologies GmbH colour scheme 
+       Grey    #565656
+       LBlue #36A9E1
+       DBlue #246DA2
+       Green #003F2C / #006C4B
+       Yellw #C8D400
+       Red     #D81036
+-->
+
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="-224 -202 882 882">
+
+<path fill="#565656" d="M 576 544 H -64 V -160 C -64 -177.673 -49.673 -192 -32 -192 H 544 C 561.673 -192 576 -177.673 576 -160 V 544 Z "/>
+
+<path fill="#ffffff" d="M 480 0 H 32 C 14.327 0 0 -14.327 0 -32 V -96 C 0 -113.673 14.327 -128 32 -128 H 480 C 497.673 -128 512 -113.673 512 -96 V -32 C 512 -14.327 497.673 0 480 0 Z M 432 -88 C 418.745 -88 408 -77.255 408 -64 S 418.745 -40 432 -40 S 456 -50.745 456 -64 S 445.255 -88 432 -88 Z M 368 -88 C 354.745 -88 344 -77.255 344 -64 S 354.745 -40 368 -40 S 392 -50.745 392 -64 S 381.255 -88 368 -88 Z "/>
+
+<path fill="#ffffff" d="M 480 160 H 32 C 14.327 160 0 145.673 0 128 V 64 C 0 46.327 14.327 32 32 32 H 480 C 497.673 32 512 46.327 512 64 V 128 C 512 145.673 497.673 160 480 160 Z M 432 72 C 418.745 72 408 82.745 408 96 S 418.745 120 432 120 S 456 109.255 456 96 S 445.255 72 432 72 Z M 368 72 C 354.745 72 344 82.745 344 96 S 354.745 120 368 120 S 392 109.255 392 96 S 381.255 72 368 72 Z "/>
+
+<path fill="#ffffff" d="M 480 320 H 32 C 14.327 320 0 305.673 0 288 V 224 C 0 206.327 14.327 192 32 192 H 480 C 497.673 192 512 206.327 512 224 V 288 C 512 305.673 497.673 320 480 320 Z M 432 232 C 418.745 232 408 242.745 408 256 S 418.745 280 432 280 S 456 269.255 456 256 S 445.255 232 432 232 Z M 368 232 C 354.745 232 344 242.745 344 256 S 354.745 280 368 280 S 392 269.255 392 256 S 381.255 232 368 232 Z "/>
+
+<path fill="#ffffff" d="M 480 480 H 32 C 14.327 480 0 465.673 0 448 V 384 C 0 366.327 14.327 352 32 352 H 480 C 497.673 352 512 366.327 512 384 V 448 C 512 465.673 497.673 480 480 480 Z M 432 392 C 418.745 392 408 402.745 408 416 S 418.745 440 432 440 S 456 429.255 456 416 S 445.255 392 432 392 Z M 368 392 C 354.745 392 344 402.745 344 416 S 354.745 440 368 440 S 392 429.255 392 416 S 381.255 392 368 392 Z"/>
+
+<path fill="#C8D400" d="M 480 670 H -96 C -155 670 -190 631 -192 574 H 576 C 575 622 543 670 480 670 Z"/>
+</svg>
index 04658a3..0276222 100644 (file)
@@ -24,78 +24,74 @@ import DialogContent from '@mui/material/DialogContent';
 import DialogContentText from '@mui/material/DialogContentText';
 import DialogTitle from '@mui/material/DialogTitle';
 
+import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect';
 import { inventoryElementsReloadAction } from '../handlers/inventoryElementsHandler';
-import { IDispatcher, connect, Connect } from '../../../../framework/src/flux/connect';
 
 import { InventoryType } from '../models/inventory';
 
 export enum RefreshInventoryDialogMode {
-  None = "none",
-  RefreshInventoryTable = "RefreshInventoryTable",
+  None = 'none',
+  RefreshInventoryTable = 'RefreshInventoryTable',
 }
 
 const mapDispatch = (dispatcher: IDispatcher) => ({
-  refreshInventory: () => dispatcher.dispatch(inventoryElementsReloadAction)
+  refreshInventory: () => dispatcher.dispatch(inventoryElementsReloadAction),
 });
 
 type DialogSettings = {
-  dialogTitle: string,
-  dialogDescription: string,
-  applyButtonText: string,
-  cancelButtonText: string,
-  enableMountIdEditor: boolean,
-  enableUsernameEditor: boolean,
-  enableExtendedEditor: boolean,
-}
+  dialogTitle: string;
+  dialogDescription: string;
+  applyButtonText: string;
+  cancelButtonText: string;
+  enableMountIdEditor: boolean;
+  enableUsernameEditor: boolean;
+  enableExtendedEditor: boolean;
+};
 
 const settings: { [key: string]: DialogSettings } = {
   [RefreshInventoryDialogMode.None]: {
-    dialogTitle: "",
-    dialogDescription: "",
-    applyButtonText: "",
-    cancelButtonText: "",
+    dialogTitle: '',
+    dialogDescription: '',
+    applyButtonText: '',
+    cancelButtonText: '',
     enableMountIdEditor: false,
     enableUsernameEditor: false,
     enableExtendedEditor: false,
   },
   [RefreshInventoryDialogMode.RefreshInventoryTable]: {
-    dialogTitle: "Do you want to refresh the Inventory table?",
-    dialogDescription: "",
-    applyButtonText: "Yes",
-    cancelButtonText: "Cancel",
+    dialogTitle: 'Do you want to refresh the Inventory table?',
+    dialogDescription: '',
+    applyButtonText: 'Yes',
+    cancelButtonText: 'Cancel',
     enableMountIdEditor: true,
     enableUsernameEditor: true,
     enableExtendedEditor: true,
-  }
-}
+  },
+};
 
 type RefreshInventoryDialogComponentProps = Connect<undefined, typeof mapDispatch> & {
   mode: RefreshInventoryDialogMode;
   onClose: () => void;
 };
 
-type RefreshInventoryDialogComponentState = InventoryType & { isNameValid: boolean, isHostSet: boolean };
+type RefreshInventoryDialogComponentState = InventoryType & { isNameValid: boolean; isHostSet: boolean };
 
 class RefreshInventoryDialogComponent extends React.Component<RefreshInventoryDialogComponentProps, RefreshInventoryDialogComponentState> {
-  constructor(props: RefreshInventoryDialogComponentProps) {
-    super(props);
-  }
-
   render(): JSX.Element {
     const setting = settings[this.props.mode];
     return (
       <Dialog open={this.props.mode !== RefreshInventoryDialogMode.None}>
-        <DialogTitle id="form-dialog-title" aria-label={`${setting.dialogTitle.replace(/ /g, "-").toLowerCase()}-dialog`}>{setting.dialogTitle}</DialogTitle>
+        <DialogTitle id="form-dialog-title" aria-label={`${setting.dialogTitle.replace(/ /g, '-').toLowerCase()}-dialog`}>{setting.dialogTitle}</DialogTitle>
         <DialogContent>
           <DialogContentText>
             {setting.dialogDescription}
           </DialogContentText>
         </DialogContent>
         <DialogActions>
-          <Button aria-label="dialog-confirm-button" onClick={(event) => {
+          <Button aria-label="dialog-confirm-button" onClick={() => {
             this.onRefresh();
           }} color="inherit" > {setting.applyButtonText} </Button>
-          <Button aria-label="dialog-cancel-button" onClick={(event) => {
+          <Button aria-label="dialog-cancel-button" onClick={() => {
             this.onCancel();
           }} color="secondary"> {setting.cancelButtonText} </Button>
         </DialogActions>
@@ -110,7 +106,7 @@ class RefreshInventoryDialogComponent extends React.Component<RefreshInventoryDi
 
   private onCancel = () => {
     this.props.onClose();
-  }
+  };
 }
 
 export const RefreshInventoryDialog = connect(undefined, mapDispatch)(RefreshInventoryDialogComponent);
index 46827e8..136b908 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
-import { InventoryTreeNode, InventoryType } from "models/inventory";
 import { convertPropertyNames, replaceHyphen } from "../../../../framework/src/utilities/yangHelper";
 
-// Tree mittels tree-level und parent UUID (incl)
-// einzelabfrage mit db-id
+import { InventoryTreeNode, InventoryType } from "../models/inventory";
+
 const data = [
   { "manufacturer-identifier": "ONF-Wireless-Transport", "version": "a2.module-newest", "uuid": "a2.module-1.1.1.5", "part-type-id": "3FE25774AA01", "model-identifier": "VAUIAEYAAA", "tree-level": 2, "node-id": "robot_sim_2_equipment", "description": "WS/CORE-MAIN/a2.module#5", "type-name": "a2.module", "serial": "0003548168", "id": "robot_sim_2_equipment/a2.module-1.1.1.5", "parent-uuid": "CARD-1.1.1.0", "contained-holder": ["SUBRACK-1.15.0.0"], "date": "2005-11-09T00:00:00.0Z" },
   { "manufacturer-identifier": "SAN", "version": "234", "uuid": "CARD-1.1.6.0", "part-type-id": "part-number-12", "model-identifier": "model-id-12", "tree-level": 1, "node-id": "robot_sim_2_equipment", "description": "WS/p8.module", "type-name": "p8.module", "serial": "serial-number-124", "id": "robot_sim_2_equipment/CARD-1.1.6.0", "parent-uuid": "SHELF-1.1.0.0", "contained-holder": ["PORT-1.1.6.5", "PORT-1.1.6.8", "PORT-1.1.6.7", "PORT-1.1.6.6"], "date": "2013-11-23T00:00:00.0Z" },
diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/handlers/connectedNetworkElementsHandler.ts b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/connectedNetworkElementsHandler.ts
deleted file mode 100644 (file)
index e6138cc..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/**
- * ============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', false, { 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);
index 0e857ff..b1a0c58 100644 (file)
 // main state handler
 
 import { combineActionHandler } from '../../../../framework/src/flux/middleware';
-
 // ** do not remove **
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
 import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
-import { IActionHandler } from '../../../../framework/src/flux/action';
 
-import { IInvenroryTree, inventoryTreeHandler } from './inventoryTreeHandler';
-import { IConnectedNetworkElementsState, connectedNetworkElementsActionHandler } from './connectedNetworkElementsHandler';
 import { PanelId } from '../models/panelId';
+import { IInventoryDeviceListState, inventoryDeviceListActionHandler } from './inventoryDeviceListActionHandler';
+import { IInventoryElementsState, inventoryElementsActionHandler } from './inventoryElementsHandler';
+import { IInvenroryTree, inventoryTreeHandler } from './inventoryTreeHandler';
 import { currentOpenPanelHandler } from './panelHandler';
-import { inventoryElementsActionHandler, IInventoryElementsState } from './inventoryElementsHandler';
 
 export interface IInventoryAppStateState {
   inventoryTree: IInvenroryTree;
-  connectedNetworkElements: IConnectedNetworkElementsState; // used for ne selection
   currentOpenPanel: PanelId;
   inventoryElements: IInventoryElementsState;
+  inventoryDeviceList: IInventoryDeviceListState;
 }
 
-
-
-
 declare module '../../../../framework/src/store/applicationStore' {
   interface IApplicationStoreState {
     inventory: IInventoryAppStateState;
@@ -47,9 +43,9 @@ declare module '../../../../framework/src/store/applicationStore' {
 
 const actionHandlers = {
   inventoryTree: inventoryTreeHandler,
-  connectedNetworkElements: connectedNetworkElementsActionHandler,
   currentOpenPanel: currentOpenPanelHandler,
-  inventoryElements: inventoryElementsActionHandler
+  inventoryElements: inventoryElementsActionHandler,
+  inventoryDeviceList: inventoryDeviceListActionHandler,
 };
 
 export const inventoryAppRootHandler = combineActionHandler<IInventoryAppStateState>(actionHandlers);
diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryDeviceListActionHandler.ts b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryDeviceListActionHandler.ts
new file mode 100644 (file)
index 0000000..7c06cad
--- /dev/null
@@ -0,0 +1,56 @@
+/**
+ * ============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 { AllInventoryDeviceListLoadedAction, LoadAllInventoryDeviceListAction } from '../actions/inventoryDeviceListActions';
+import { InventoryDeviceListType } from '../models/inventoryDeviceListType';
+
+export interface IInventoryDeviceListState {
+  inventoryDeviceList: InventoryDeviceListType[];
+  busy: boolean;
+}
+
+const inventoryDeviceListListStateInit: IInventoryDeviceListState = {
+  inventoryDeviceList: [],
+  busy: false,
+};
+
+export const inventoryDeviceListActionHandler: IActionHandler<IInventoryDeviceListState> = (state = inventoryDeviceListListStateInit, action) => {
+  if (action instanceof LoadAllInventoryDeviceListAction) {
+
+    state = {
+      ...state,
+      busy: true,
+    };
+
+  } else if (action instanceof AllInventoryDeviceListLoadedAction) {
+    if (!action.error && action.inventoryDeviceList) {
+      state = {
+        ...state,
+        inventoryDeviceList: action.inventoryDeviceList,
+        busy: false,
+      };
+    } else {
+      state = {
+        ...state,
+        busy: false,
+      };
+    }
+  }
+  return state;
+};
\ No newline at end of file
index a65319e..7bac8f6 100644 (file)
@@ -15,7 +15,7 @@
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import { createExternal,IExternalTableState } from '../../../../framework/src/components/material-table/utilities';
+import { createExternal, IExternalTableState } from '../../../../framework/src/components/material-table/utilities';
 import { createSearchDataHandler } from '../../../../framework/src/utilities/elasticSearch';
 
 import { InventoryType } from '../models/inventory';
@@ -23,7 +23,7 @@ import { InventoryType } from '../models/inventory';
 export interface IInventoryElementsState extends IExternalTableState<InventoryType> { }
 
 // create eleactic search material data fetch handler
-const inventoryElementsSearchHandler = createSearchDataHandler<InventoryType>("inventory");
+const inventoryElementsSearchHandler = createSearchDataHandler<InventoryType>('inventory');
 
 export const {
   actionHandler: inventoryElementsActionHandler,
index 9029a67..fe90d98 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
-import { IActionHandler } from "../../../../framework/src/flux/action";
+import { IActionHandler } from '../../../../framework/src/flux/action';
 
-import { SetBusyAction, UpdateInventoryTreeAction, UpdateSelectedNodeAction, SetSearchTextAction, UpdateExpandedNodesAction } from "../actions/inventoryTreeActions";
-import { InventoryTreeNode, InventoryType, TreeDemoItem } from "../models/inventory";
+import { SetBusyAction, SetSearchTextAction, UpdateExpandedNodesAction, UpdateInventoryTreeAction, UpdateSelectedNodeAction } from '../actions/inventoryTreeActions';
+import { InventoryTreeNode, InventoryType, TreeDemoItem } from '../models/inventory';
 
 
 export interface IInvenroryTree {
@@ -33,10 +33,10 @@ export interface IInvenroryTree {
 const initialState: IInvenroryTree = {
   isBusy: false,
   rootNodes: [],
-  searchTerm: "",
+  searchTerm: '',
   selectedNode: undefined,
   expandedItems: [],
-}
+};
 
 
 const getTreeDataFromInvetoryTreeNode = (node: InventoryTreeNode): TreeDemoItem[] => Object.keys(node).reduce<TreeDemoItem[]>((acc, key) => {
@@ -61,8 +61,8 @@ export const inventoryTreeHandler: IActionHandler<IInvenroryTree> = (state = ini
   } else if (action instanceof UpdateSelectedNodeAction) {
     state = { ...state, selectedNode: action.selectedNode };
   } else if (action instanceof UpdateExpandedNodesAction) {
-    state = { ...state, expandedItems: action.expandedNodes || []}
+    state = { ...state, expandedItems: action.expandedNodes || [] };
   }
 
   return state;
-}
\ No newline at end of file
+};
\ No newline at end of file
index 7612531..7912d0e 100644 (file)
@@ -1,11 +1,11 @@
-import { PanelId } from "../models/panelId";
-import { IActionHandler } from "../../../../framework/src/flux/action";
-import { SetPanelAction } from "../actions/panelActions";
+import { IActionHandler } from '../../../../framework/src/flux/action';
 
+import { SetPanelAction } from '../actions/panelActions';
+import { PanelId } from '../models/panelId';
 
 export const currentOpenPanelHandler: IActionHandler<PanelId> = (state = null, action) => {
-    if (action instanceof SetPanelAction) {
-      state = action.panelId;
-    }
-    return state;
-  }
\ No newline at end of file
+  if (action instanceof SetPanelAction) {
+    state = action.panelId;
+  }
+  return state;
+};
\ No newline at end of file
index a6c9905..a09fd7e 100644 (file)
@@ -35,7 +35,7 @@ export type InventoryType = {
   partTypeId: string;
   modelIdentifier: string;
   typeName: string;
-}
+};
 
 export type InventoryTreeNode = {
   [key: string]: {
@@ -44,7 +44,7 @@ export type InventoryTreeNode = {
     isMatch?: boolean;
     ownSeverity?: string;
     childrenSeveritySummary?: string;
-  }
-}
+  };
+};
 
 export type TreeDemoItem = ExternalTreeItem<string>;
\ No newline at end of file
diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/models/inventoryDeviceListType.ts b/sdnr/wt/odlux/apps/inventoryApp/src/models/inventoryDeviceListType.ts
new file mode 100644 (file)
index 0000000..ab24114
--- /dev/null
@@ -0,0 +1,25 @@
+/**
+ * ============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==========================================================================
+ */
+
+/**
+ * Represents all the distinct devices from the inventory history data.
+ */
+
+export type InventoryDeviceListType = {
+  nodeId: string;
+};
index 88f7018..e1ef1ea 100644 (file)
@@ -24,7 +24,7 @@ export type NetworkElementConnection = {
   username?: string;
   password?: string;
   isRequired?: boolean;
-  status?: "connected" | "mounted" | "unmounted" | "connecting" | "disconnected" | "idle";
+  status?: 'connected' | 'mounted' | 'unmounted' | 'connecting' | 'disconnected' | 'idle';
   coreModelCapability?: string;
   deviceType?: string;
   nodeDetails?: {
@@ -33,5 +33,5 @@ export type NetworkElementConnection = {
       failureReason: string;
       capability: string;
     }[];
-  }
-}
+  };
+};
index b05c1db..8f8224c 100644 (file)
@@ -16,4 +16,4 @@
  * ============LICENSE_END==========================================================================
  */
 
-export type PanelId = null | "InventoryElementsTable" | "TreeviewTable";
\ No newline at end of file
+export type PanelId = null | 'Equipment' | 'TreeView';
\ No newline at end of file
index 50339c0..8198599 100644 (file)
 * ============LICENSE_END==========================================================================
 */
 // app configuration and main entry point for the app
+import React from 'react';
+import { Redirect, Route, RouteComponentProps, Switch, withRouter } from 'react-router-dom';
 
-import * as React from "react";
-import { withRouter, RouteComponentProps, Route, Switch, Redirect } from 'react-router-dom';
-import { faShoppingBag } from '@fortawesome/free-solid-svg-icons'; // select app icon
+import { connect, Connect, IDispatcher } from '../../../framework/src/flux/connect';
 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 { IApplicationStoreState } from '../../../framework/src/store/applicationStore';
+import { SetPanelAction } from './actions/panelActions';
+import inventoryAppRootHandler from './handlers/inventoryAppRootHandler';
+import { createInventoryElementsActions, createInventoryElementsProperties } from './handlers/inventoryElementsHandler';
+import { PanelId } from './models/panelId';
 import Dashboard from './views/dashboard';
+import { InventoryTreeView } from './views/treeview';
 
-import { PanelId } from "./models/panelId";
-import { SetPanelAction } from "./actions/panelActions";
-
-import inventoryAppRootHandler from './handlers/inventoryAppRootHandler';
-import { createInventoryElementsActions, createInventoryElementsProperties } from "./handlers/inventoryElementsHandler";
-import { createConnectedNetworkElementsProperties, createConnectedNetworkElementsActions } from "./handlers/connectedNetworkElementsHandler";
+const appIcon = require('./assets/icons/inventoryAppIcon.svg');  // select app icon
 
 let currentMountId: string | undefined = undefined;
 const mapProps = (state: IApplicationStoreState) => ({
   inventoryProperties: createInventoryElementsProperties(state),
   panelId: state.inventory.currentOpenPanel,
-  connectedNetworkElementsProperties: createConnectedNetworkElementsProperties(state),
 });
 
-const mapDisp = (dispatcher: IDispatcher) => ({
+const mapDispatch = (dispatcher: IDispatcher) => ({
   inventoryActions: createInventoryElementsActions(dispatcher.dispatch, true),
-  connectedNetworkElementsActions: createConnectedNetworkElementsActions(dispatcher.dispatch, true),
   setCurrentPanel: (panelId: PanelId) => dispatcher.dispatch(new SetPanelAction(panelId)),
 });
 
-const InventoryTableApplicationRouteAdapter = connect(mapProps, mapDisp)((props: RouteComponentProps<{ mountId?: string }> & Connect<typeof mapProps, typeof mapDisp>) => {
+const InventoryTableApplicationRouteAdapter = connect(mapProps, mapDispatch)((props: RouteComponentProps<{ mountId?: string }> & Connect<typeof mapProps, typeof mapDispatch>) => {
   if (currentMountId !== props.match.params.mountId) {
     // route parameter has changed
     currentMountId = props.match.params.mountId || undefined;
@@ -56,26 +51,20 @@ const InventoryTableApplicationRouteAdapter = connect(mapProps, mapDisp)((props:
       if (currentMountId) {
         if (props.panelId) {
           props.setCurrentPanel(props.panelId);
+        } else {
+          props.setCurrentPanel('Equipment');
         }
-        else {
-          props.setCurrentPanel("InventoryElementsTable");
-        }
-        props.inventoryActions.onFilterChanged("nodeId", currentMountId);
-        props.connectedNetworkElementsActions.onFilterChanged("nodeId", currentMountId);
+        props.inventoryActions.onFilterChanged('nodeId', currentMountId);
         if (!props.inventoryProperties.showFilter) {
           props.inventoryActions.onToggleFilter(false);
         }
-        if (!props.connectedNetworkElementsProperties.showFilter) {
-          props.connectedNetworkElementsActions.onToggleFilter(false);
-        }
         props.inventoryActions.onRefresh();
-        props.connectedNetworkElementsActions.onRefresh();
       }
     });
   }
   return (
     <Dashboard />
-  )
+  );
 });
 
 const App = withRouter((props: RouteComponentProps) => (
@@ -89,11 +78,11 @@ const App = withRouter((props: RouteComponentProps) => (
 
 export function register() {
   applicationManager.registerApplication({
-    name: "inventory",
-    icon: faShoppingBag,
+    name: 'inventory',
+    icon: appIcon,
     rootActionHandler: inventoryAppRootHandler,
     rootComponent: App,
-    menuEntry: "Inventory"
+    menuEntry: 'Inventory',
   });
 }
 
index cb70bf5..4014fcf 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
+import { Result } from '../../../../framework/src/models/elasticSearch';
 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';
+import { InventoryDeviceListType } from '../models/inventoryDeviceListType';
 
 /**
  * Represents a web api accessor service for all maintenence entries related actions.
  */
 class InventoryService {
-  public async getInventoryTree(mountId: string, searchTerm: string = ""): Promise<InventoryTreeNode | null> {
+  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
+      'query': searchTerm,
     };
-    const inventoryTree = await requestRest<InventoryTreeNode>(path, { method: "POST" , body: JSON.stringify(body)});
+    const inventoryTree = await requestRest<InventoryTreeNode>(path, { method: 'POST', body: JSON.stringify(body) });
     return inventoryTree && inventoryTree || null;
   }
 
   public async getInventoryEntry(id: string): Promise<InventoryType | undefined> {
-    const path = `/rests/operations/data-provider:read-inventory-list`;
+    const path = '/rests/operations/data-provider:read-inventory-list';
     const body = {
-      "data-provider:input": {
-        "filter": [
-          { property: "id", filtervalue: id },
+      'data-provider:input': {
+        'filter': [
+          { property: 'id', filtervalue: id },
         ],
-        "sortorder": [],
-        "pagination": {
-          "size": 1,
-          "page": 1
-        }
-      }
+        'sortorder': [],
+        'pagination': {
+          'size': 1,
+          'page': 1,
+        },
+      },
     };
     const inventoryTreeElement = await requestRest<{
-      "data-provider:output": {
-        "pagination": {
-          "size": number,
-          "page": number,
-          "total": number
+      'data-provider:output': {
+        'pagination': {
+          'size': number;
+          'page': number;
+          'total': number;
+        };
+        'data': InventoryType[];
+      };
+    }>(path, { method: 'POST', body: JSON.stringify(body) });
+
+    return inventoryTreeElement && inventoryTreeElement['data-provider:output'] && inventoryTreeElement['data-provider:output'].pagination && inventoryTreeElement['data-provider:output'].pagination.total >= 1 &&
+      inventoryTreeElement['data-provider:output'].data && inventoryTreeElement['data-provider:output'].data[0] || undefined;
+    // return await getElement(id);
+  }
+
+  /**
+   * Gets all nodes from the inventory device list.
+   */
+  public async getInventoryDeviceList(): Promise<(InventoryDeviceListType)[] | null> {
+    const path = '/rests/operations/data-provider:read-inventory-device-list';
+    const query = {
+      'data-provider:input': {
+        'filter': [],
+        'sortorder': [],
+        'pagination': {
+          'size': 20,
+          'page': 1,
         },
-        "data": InventoryType[]
-      }
-    }>(path, { method: "POST", body: JSON.stringify(body) });
+      },
+    };
 
-    return inventoryTreeElement && inventoryTreeElement["data-provider:output"] && inventoryTreeElement["data-provider:output"].pagination && inventoryTreeElement["data-provider:output"].pagination.total >= 1 &&
-      inventoryTreeElement["data-provider:output"].data && inventoryTreeElement["data-provider:output"].data[0] || undefined;
-   // return await getElement(id);
+    const result = await requestRest<Result<any>>(path, { method: 'POST', body: JSON.stringify(query) });
+    return result && result['data-provider:output'] && result['data-provider:output'].data && result['data-provider:output'].data.map(ne => ({
+      nodeId: ne,
+    })) || null;
   }
 
 }
index 284f702..acd2c62 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
-import * as React from 'react';
+import React from 'react';
 import { RouteComponentProps, withRouter } from 'react-router-dom';
 
-import connect, { IDispatcher, Connect } from "../../../../framework/src/flux/connect";
-import { IApplicationStoreState } from "../../../../framework/src/store/applicationStore";
-import { MaterialTable, MaterialTableCtorType, ColumnType } from "../../../../framework/src/components/material-table";
-import { AppBar, Tabs, Tab, MenuItem, Typography } from "@mui/material";
 import Refresh from '@mui/icons-material/Refresh';
-import { PanelId } from "../models/panelId";
-import { setPanelAction } from "../actions/panelActions";
+import { AppBar, MenuItem, Tab, Tabs, Typography } from '@mui/material';
 
-
-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 { ColumnType, MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table';
+import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect';
+import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
+
+import { loadAllInventoryDeviceListAsync } from '../actions/inventoryDeviceListActions';
 import { updateInventoryTreeAsyncAction } from '../actions/inventoryTreeActions';
+import { setPanelAction } from '../actions/panelActions';
 import RefreshInventoryDialog, { RefreshInventoryDialogMode } from '../components/refreshInventoryDialog';
+import { createInventoryElementsActions, createInventoryElementsProperties } from '../handlers/inventoryElementsHandler';
+import { InventoryType } from '../models/inventory';
+import { InventoryDeviceListType } from '../models/inventoryDeviceListType';
+import { PanelId } from '../models/panelId';
 
 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
+  inventoryElements: state.inventory.inventoryElements,
+  inventoryDeviceList: state.inventory.inventoryDeviceList.inventoryDeviceList,
 });
 
 const mapDispatch = (dispatcher: IDispatcher) => ({
-  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, searchTerm?: string) => dispatcher.dispatch(updateInventoryTreeAsyncAction(mountId, searchTerm)),
+  getAllInventoryDeviceList: async () => {
+    await dispatcher.dispatch(loadAllInventoryDeviceListAsync);
+  },
 });
 
 let treeViewInitialSorted = false;
 let inventoryInitialSorted = false;
 
-const ConnectedElementTable = MaterialTable as MaterialTableCtorType<NetworkElementConnection>;
+const InventoryDeviceListTable = MaterialTable as MaterialTableCtorType<InventoryDeviceListType>;
 
 type DashboardComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDispatch>;
 type DashboardComponentState = {
-  refreshInventoryEditorMode: RefreshInventoryDialogMode
-}
+  refreshInventoryEditorMode: RefreshInventoryDialogMode;
+};
 
 class DashboardSelectorComponent extends React.Component<DashboardComponentProps, DashboardComponentState> {
   constructor(props: DashboardComponentProps) {
     super(props);
 
     this.state = {
-      refreshInventoryEditorMode: RefreshInventoryDialogMode.None
+      refreshInventoryEditorMode: RefreshInventoryDialogMode.None,
     };
   }
 
   private onHandleTabChange = (event: React.SyntheticEvent, newValue: PanelId) => {
     this.onTogglePanel(newValue);
-  }
+  };
 
   private onTogglePanel = (panelId: PanelId) => {
     const nextActivePanel = panelId;
     this.props.switchActivePanel(nextActivePanel);
 
     switch (nextActivePanel) {
-      case 'InventoryElementsTable':
+      case 'Equipment':
 
         if (!inventoryInitialSorted) {
-          this.props.inventoryElementsActions.onHandleExplicitRequestSort("nodeId", "asc");
+          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();
-        }
+      case 'TreeView':
+        this.props.getAllInventoryDeviceList();
         break;
       case null:
         // do nothing if all panels are closed
         break;
       default:
-        console.warn("Unknown nextActivePanel [" + nextActivePanel + "] in connectView");
+        console.warn('Unknown nextActivePanel [' + nextActivePanel + '] in connectView');
         break;
     }
 
@@ -116,47 +110,47 @@ class DashboardSelectorComponent extends React.Component<DashboardComponentProps
 
   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>,
+      <MenuItem aria-label={'inventory-button'} onClick={() => { this.props.updateInventoryTree(rowData.nodeId, rowData.uuid); this.props.navigateToApplication('inventory', rowData.nodeId); }}><Typography>View in Treeview</Typography></MenuItem>,
     ];
 
-  }
+  };
 
   render() {
 
     const refreshInventoryAction = {
       icon: Refresh, tooltip: 'Refresh Inventory', ariaLabel: 'refresh', onClick: () => {
         this.setState({
-          refreshInventoryEditorMode: RefreshInventoryDialogMode.RefreshInventoryTable
+          refreshInventoryEditorMode: RefreshInventoryDialogMode.RefreshInventoryTable,
         });
-      }
+      },
     };
     const { panelId: activePanelId } = this.props;
     return (
       <>
         <AppBar enableColorOnDark position="static">
           <Tabs indicatorColor="secondary" textColor="inherit" value={activePanelId} onChange={this.onHandleTabChange} aria-label="inventory-app-tabs">
-            <Tab label="Table View" value="InventoryElementsTable" aria-label="table-tab" />
-            <Tab label="Tree view" value="TreeviewTable" aria-label="treeview-tab" />
+            <Tab label="Equipment" value="Equipment" aria-label="equipment-tab" />
+            <Tab label="Tree View" value="TreeView" aria-label="treeview-tab" />
           </Tabs>
         </AppBar>
 
         {
 
-          activePanelId === "InventoryElementsTable" &&
+          activePanelId === 'Equipment' &&
           <>
-            <InventoryTable stickyHeader title="Inventory" idProperty="_id" tableId="inventory-table" customActionButtons={[refreshInventoryAction]} 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" },
+            <InventoryTable stickyHeader idProperty="_id" tableId="inventory-table" customActionButtons={[refreshInventoryAction]} 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 => {
 
@@ -171,22 +165,20 @@ class DashboardSelectorComponent extends React.Component<DashboardComponentProps
 
         }
         {
-          activePanelId === "TreeviewTable" &&
-
-          <ConnectedElementTable stickyHeader tableId="treeview-networkelement-selection-table"
-            onHandleClick={(e, row) => {
-              this.props.navigateToApplication("inventory", row.nodeId);
-              this.props.updateInventoryTree(row.nodeId, '*');
-            }}
-            columns={[
-              { property: "nodeId", title: "Node 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>
+          activePanelId === 'TreeView' &&
+          <>
+            <InventoryDeviceListTable stickyHeader tableId="treeview-networkelement-selection-table"
+              defaultSortColumn={'nodeId'} defaultSortOrder="asc"
+              onHandleClick={(e, row) => {
+                this.props.navigateToApplication('inventory', row.nodeId);
+                this.props.updateInventoryTree(row.nodeId, '*');
+              }}
+              rows={this.props.inventoryDeviceList} asynchronus
+              columns={[
+                { property: 'nodeId', title: 'Node Name', type: ColumnType.text },
+              ]} idProperty="nodeId" >
+            </InventoryDeviceListTable>
+          </>
         }
       </>
     );
@@ -194,15 +186,14 @@ class DashboardSelectorComponent extends React.Component<DashboardComponentProps
 
   private onCloseRefreshInventoryDialog = () => {
     this.setState({
-      refreshInventoryEditorMode: RefreshInventoryDialogMode.None
+      refreshInventoryEditorMode: RefreshInventoryDialogMode.None,
     });
-  }
-  componentDidMount() {
+  };
 
+  componentDidMount() {
     if (this.props.panelId === null) { //set default tab if none is set
-      this.onTogglePanel("InventoryElementsTable");
+      this.onTogglePanel('Equipment');
     }
-
   }
 }
 
index 2526639..8d47ec3 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from "react";
-import { withRouter, RouteComponentProps } from 'react-router-dom';
+import React from 'react';
+import { RouteComponentProps, withRouter } from 'react-router-dom';
 
 import Button from '@mui/material/Button';
 import { Theme } from '@mui/material/styles'; // infra for styling
-
 import { WithStyles } from '@mui/styles';
-import withStyles from '@mui/styles/withStyles';
 import createStyles from '@mui/styles/createStyles';
+import withStyles from '@mui/styles/withStyles';
 
 const styles = (theme: Theme) => createStyles({
   warnButton: {
-    backgroundColor: theme.palette.primary.dark
-  }
+    backgroundColor: theme.palette.primary.dark,
+  },
 });
 
 type DetailProps = RouteComponentProps<{ id: string }> & WithStyles<typeof styles>;
@@ -37,8 +36,8 @@ export const Detail = withStyles( styles )( withRouter( (props: DetailProps) =>
   <div>
     <h1>Detail {props.match.params.id}</h1>
     <p>This are the information about {props.staticContext}.</p>
-    <Button color={"secondary"} variant={"contained"}>Start</Button>
-    <Button color="inherit" className={ props.classes.warnButton } variant={"contained"}>Stop</Button>
+    <Button color={'secondary'} variant={'contained'}>Start</Button>
+    <Button color="inherit" className={ props.classes.warnButton } variant={'contained'}>Stop</Button>
   </div>
 )));
 
index b0e962d..954c074 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from "react";
-import { Theme } from '@mui/material/styles';
+import React from 'react';
 
+import Breadcrumbs from '@mui/material/Breadcrumbs';
+import Link from '@mui/material/Link';
+import { Theme } from '@mui/material/styles';
 import { WithStyles } from '@mui/styles';
-import withStyles from '@mui/styles/withStyles';
 import createStyles from '@mui/styles/createStyles';
-
+import withStyles from '@mui/styles/withStyles';
+import { RouteComponentProps } from 'react-router-dom';
+import { SearchMode, TreeView, TreeViewCtorType } from '../../../../framework/src/components/material-ui/treeView';
 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 { IApplicationStoreState } from "../../../../framework/src/store/applicationStore";
-
-import Breadcrumbs from '@mui/material/Breadcrumbs';
-import Link from '@mui/material/Link';
-
-import { updateInventoryTreeAsyncAction, selectInventoryNodeAsyncAction, UpdateSelectedNodeAction, UpdateExpandedNodesAction, setSearchTermAction } from "../actions/inventoryTreeActions";
-import { TreeDemoItem } from "../models/inventory";
-
-import { RouteComponentProps } from 'react-router-dom';
+import { selectInventoryNodeAsyncAction, setSearchTermAction, UpdateExpandedNodesAction, updateInventoryTreeAsyncAction, UpdateSelectedNodeAction } from '../actions/inventoryTreeActions';
+import { TreeDemoItem } from '../models/inventory';
 
 const styles = (theme: Theme) => createStyles({
   root: {
-    flex: "1 0 0%",
-    display: "flex",
-    flexDirection: "row",
+    flex: '1 0 0%',
+    display: 'flex',
+    flexDirection: 'row',
   },
   tree: {
-    flex: "1 0 0%",
-    minWidth: "250px",
-    padding: `0px ${theme.spacing(1)}`
+    wordWrap: 'break-word',
+    minWidth: '250px',
+    padding: `0px ${theme.spacing(1)}`,
   },
   details: {
-    flex: "5 0 0%",
-    padding: `0px ${theme.spacing(1)}`
-  }
+    flex: '5 0 0%',
+    padding: `0px ${theme.spacing(1)}`,
+  },
 });
 
 const mapProps = (state: IApplicationStoreState) => ({
@@ -68,19 +64,19 @@ const mapDispatch = (dispatcher: IDispatcher) => ({
   setSearchTerm: (searchTerm: string) => dispatcher.dispatch(setSearchTermAction(searchTerm)),
 });
 
-const propsChache = Symbol("PropsCache");
+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 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> {
@@ -96,14 +92,15 @@ class DashboardComponent extends React.Component<TreeviewComponentProps, Treevie
 
   static getDerivedStateFromProps(props: TreeviewComponentProps, state: TreeviewComponentState) {
     if (state[propsChache].rootNodes != props.rootNodes) {
-      state = { ...state, rootNodes: props.rootNodes }
+      // eslint-disable-next-line no-param-reassign
+      state = { ...state, rootNodes: props.rootNodes };
     }
     return state;
   }
 
   render() {
     const { classes, updateInventoryTree, updateExpendedNodes, expendedItems, selectedNode, selectTreeNode, searchTerm, match: { params: { mountId } } } = this.props;
-    const scrollbar = { overflow: "auto", paddingRight: "20px" }
+    const scrollbar = { overflow: 'auto', paddingRight: '20px' };
 
     let filteredDashboardPath = `/inventory/dashboard/${this.props.match.params.mountId}`;
     let basePath = `/inventory/${this.props.match.params.mountId}`;
@@ -128,6 +125,7 @@ class DashboardComponent extends React.Component<TreeviewComponentProps, Treevie
         <br />
         <div style={scrollbar} className={classes.root}>
           <InventoryTree className={classes.tree} items={this.state.rootNodes} enableSearchBar initialSearchTerm={searchTerm} searchMode={SearchMode.OnEnter} searchTerm={searchTerm}
+            // eslint-disable-next-line @typescript-eslint/no-shadow
             onSearch={(searchTerm) => updateInventoryTree(mountId, searchTerm)} expandedItems={expendedItems} onFolderClick={(item) => {
               const indexOfItemToToggle = expendedItems.indexOf(item);
               if (indexOfItemToToggle === -1) {
@@ -141,20 +139,15 @@ class DashboardComponent extends React.Component<TreeviewComponentProps, Treevie
             }}
             onItemClick={(elm) => selectTreeNode(elm.value)} />
           <div className={classes.details}>{
-            selectedNode && renderObject(selectedNode, "tree-view") || null
+            selectedNode && renderObject(selectedNode, 'tree-view') || null
           }</div>
         </div>
       </div>
     );
   }
 
-  componentDidMount() {
-    const { updateInventoryTree, searchTerm, match: { params: { mountId } } } = this.props;
-    updateInventoryTree(mountId, searchTerm);
-  }
-
   componentWillUnmount() {
-    this.props.setSearchTerm("*");
+    this.props.setSearchTerm('*');
   }
 }
 
index a66b5d8..ca65092 100644 (file)
@@ -4,7 +4,7 @@
     "outDir": "./dist",
     "sourceMap": true,
     "forceConsistentCasingInFileNames": true,
-    "allowSyntheticDefaultImports": false,
+    "allowSyntheticDefaultImports": true,
     "allowUnreachableCode": false,
     "allowUnusedLabels": false,
     "noFallthroughCasesInSwitch": true,
index 403cc53..6a78056 100644 (file)
@@ -57,6 +57,16 @@ module.exports = (env) => {
         use: [{
           loader: "babel-loader"
         }]
+      },{
+        //don't minify images
+        test: /\.(png|gif|jpg|svg)$/,
+        use: [{
+          loader: 'url-loader',
+          options: {
+            limit: 10,
+            name: './images/[name].[ext]'
+          }
+        }]
       }]
     },
 
@@ -127,37 +137,37 @@ module.exports = (env) => {
       },
       proxy: {
         "/oauth2/": {
-          target: "http://localhost:8181",
+          target: "http://sdnc-web:8080",
           secure: false
         },
         "/database/": {
-          target: "http://localhost:8181",
+          target: "http://sdnc-web:8080",
           secure: false
         },
         "/restconf/": {
-          target: "http://localhost:8181",
+          target: "http://sdnc-web:8080",
           secure: false
         },
         "/rests/": {
-          target: "http://localhost:8181",
+          target: "http://sdnc-web:8080",
           secure: false
         },
         "/help/": {
-          target: "http://localhost:8181",
+          target: "http://sdnc-web:8080",
           secure: false
         },
         "/tree/": {
-          target: "http://localhost:8181",
+          target: "http://sdnc-web:8080",
           secure: false
         },
         "/websocket": {
-          target: "http://localhost:8181",
+          target: "http://sdnc-web:8080",
           ws: true,
           changeOrigin: true,
           secure: false
         },
         "/yang-schema": {
-          target: "http://localhost:8181",
+          target: "http://sdnc-web:8080",
           ws: true,
           changeOrigin: true,
           secure: false
index dd678d6..d7c3254 100644 (file)
@@ -39,6 +39,8 @@
     "jquery": "3.3.1",
     "react": "17.0.2",
     "react-dom": "17.0.2",
-    "react-router-dom": "5.2.0"
+    "react-router-dom": "5.2.0",
+    "@fortawesome/free-solid-svg-icons": "5.6.3",
+    "@fortawesome/react-fontawesome": "0.1.14"
   }
 }
index f70e5fd..b296a0a 100644 (file)
@@ -19,6 +19,7 @@
   ~ ============LICENSE_END=======================================================
   ~
   -->
+
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
                         <!-- optional: default phase is "generate-resources" -->
                         <phase>initialize</phase>
                         <configuration>
-                            <nodeVersion>v12.13.0</nodeVersion>
+                            <nodeVersion>v12.22.0</nodeVersion>
                             <yarnVersion>v1.22.10</yarnVersion>
                         </configuration>
                     </execution>
index 162d943..740abff 100644 (file)
@@ -1,3 +1,4 @@
+/* eslint-disable @typescript-eslint/no-unused-expressions */
 /**
  * ============LICENSE_START========================================================================
  * ONAP : ccsdk feature sdnr wt odlux
  * the License.
  * ============LICENSE_END==========================================================================
  */
+import { AddSnackbarNotification } from '../../../../framework/src/actions/snackbarActions';
 import { Action } from '../../../../framework/src/flux/action';
 import { Dispatch } from '../../../../framework/src/flux/store';
-import { MaintenenceEntry, spoofSymbol } from '../models/maintenenceEntryType';
-
-import { AddSnackbarNotification } from '../../../../framework/src/actions/snackbarActions';
-import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
-
 
+import { maintenanceEntriesReloadAction } from '../handlers/maintenanceEntriesHandler';
+import { MaintenanceEntry, spoofSymbol } from '../models/maintenanceEntryType';
 import { maintenenceService } from '../services/maintenenceService';
-import { maintenanceEntriesReloadAction } from '../handlers/maintenenceEntriesHandler';
 
 export class BaseAction extends Action { }
 
@@ -32,7 +30,7 @@ export class LoadAllMainteneceEntriesAction extends BaseAction { }
 
 export class AllMainteneceEntriesLoadedAction extends BaseAction {
 
-  constructor (public maintenenceEntries: MaintenenceEntry[] | null, error?:string) {
+  constructor(public maintenenceEntries: MaintenanceEntry[] | null) {
     super();
 
   }
@@ -40,39 +38,39 @@ export class AllMainteneceEntriesLoadedAction extends BaseAction {
 
 
 export class UpdateMaintenanceEntry extends BaseAction {
-  constructor(public maintenenceEntry: MaintenenceEntry) {
+  constructor(public maintenenceEntry: MaintenanceEntry) {
     super();
   }
 }
 
 /** Represents an async thunk action creator to add an element to the maintenence entries. */
-export const addOrUpdateMaintenenceEntryAsyncActionCreator = (entry: MaintenenceEntry) => (dispatch: Dispatch) => {
+export const addOrUpdateMaintenenceEntryAsyncActionCreator = (entry: MaintenanceEntry) => (dispatch: Dispatch) => {
   maintenenceService.writeMaintenenceEntry(entry).then(result => {
     result && window.setTimeout(() => {
       // dispatch(loadAllMountedNetworkElementsAsync);
       dispatch(new UpdateMaintenanceEntry(entry));
-      dispatch(new AddSnackbarNotification({ message: `Successfully ${result && result.created ? "created" : "updated"} maintenance settings for [${entry.nodeId}]`, options: { variant: 'success' } }));
+      dispatch(new AddSnackbarNotification({ message: `Successfully ${result && result.created ? 'created' : 'updated'} maintenance settings for [${entry.nodeId}]`, options: { variant: 'success' } }));
     }, 900);
-    dispatch(maintenanceEntriesReloadAction)
+    dispatch(maintenanceEntriesReloadAction);
   });
 };
 
 /** Represents an async thunk action creator to delete an element from the maintenence entries. */
-export const removeFromMaintenenceEntrysAsyncActionCreator = (entry: MaintenenceEntry) => (dispatch: Dispatch) => {
+export const removeFromMaintenenceEntrysAsyncActionCreator = (entry: MaintenanceEntry) => (dispatch: Dispatch) => {
   maintenenceService.deleteMaintenenceEntry(entry).then(result => {
     result && window.setTimeout(() => {
       dispatch(new UpdateMaintenanceEntry({
         [spoofSymbol]: true,
-        _id: entry._id,
+        mId: entry.mId,
         nodeId: entry.nodeId,
-        description: "",
-        start: "",
-        end: "",
-        active: false
+        description: '',
+        start: '',
+        end: '',
+        active: false,
       }));
       dispatch(new AddSnackbarNotification({ message: `Successfully removed [${entry.nodeId}]`, options: { variant: 'success' } }));
     }, 900);
-    dispatch(maintenanceEntriesReloadAction)
+    dispatch(maintenanceEntriesReloadAction);
   });
 };
 
diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src/assets/icons/maintenanceAppIcon.svg b/sdnr/wt/odlux/apps/maintenanceApp/src/assets/icons/maintenanceAppIcon.svg
new file mode 100644 (file)
index 0000000..8b99a5e
--- /dev/null
@@ -0,0 +1,50 @@
+<!-- highstreet technologies GmbH colour scheme 
+       Grey    #565656
+       LBlue #36A9E1
+       DBlue #246DA2
+       Green #003F2C / #006C4B
+       Yellw #C8D400
+       Red     #D81036
+-->
+
+<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 684.000000 684.000000">
+
+<g transform="translate(0.000000,684.000000) scale(0.100000,-0.100000)">
+
+<path fill="#565656" d="M3044 6693 c-12 -2 -33 -17 -48 -32 -35 -37 -55 -136 -96 -476 -17
+-142 -32 -260 -33 -260 -1 -1 -60 -19 -132 -40 -243 -71 -406 -139 -600 -250
+-99 -57 -120 -66 -138 -57 -12 6 -124 91 -249 189 -124 98 -252 193 -283 212
+-57 34 -57 34 -89 17 -81 -43 -421 -374 -513 -500 -24 -32 -43 -67 -43 -78 0
+-36 98 -173 337 -472 57 -70 103 -132 103 -138 0 -5 -17 -35 -39 -66 -98 -144
+-275 -566 -301 -715 -14 -78 26 -65 -335 -112 -384 -51 -423 -62 -447 -125 -5
+-14 -9 -179 -9 -376 0 -414 0 -416 78 -433 43 -10 213 -35 518 -77 183 -25
+167 -12 221 -182 75 -238 160 -433 278 -637 l35 -61 -175 -219 c-206 -259
+-274 -357 -274 -393 0 -22 50 -76 258 -282 142 -141 275 -269 295 -284 l36
+-28 38 23 c62 38 177 124 380 286 l193 153 92 -57 c168 -102 383 -193 633
+-269 66 -19 123 -39 127 -43 9 -8 19 -76 53 -361 14 -113 33 -243 42 -289 23
+-119 0 -114 455 -113 237 0 373 4 386 11 44 23 61 101 106 485 l32 269 149 46
+c227 71 395 139 395 160 0 5 -125 127 -277 272 -194 186 -283 263 -297 262
+-12 -1 -75 -11 -141 -23 -318 -56 -700 -29 -973 69 -278 101 -476 226 -685
+435 -275 275 -445 609 -504 988 -23 152 -23 432 1 586 60 394 215 705 493 984
+166 169 314 278 498 368 292 143 539 191 912 176 194 -8 297 -24 446 -72 485
+-156 879 -500 1098 -959 134 -278 196 -617 170 -918 -6 -73 -21 -186 -33 -251
+l-22 -119 274 -268 c268 -261 275 -268 292 -248 23 27 88 198 133 351 31 104
+41 127 59 132 11 3 131 19 266 36 402 50 463 65 477 118 5 15 8 192 8 393 0
+353 -1 368 -20 389 -36 40 -79 49 -469 100 -132 18 -249 36 -261 40 -18 7 -29
+33 -59 137 -66 223 -139 392 -292 669 l-28 51 126 159 c193 246 265 343 294
+399 l27 52 -31 45 c-101 147 -505 538 -555 538 -30 0 -234 -146 -506 -362
+l-104 -82 -51 28 c-244 136 -390 201 -606 271 -63 21 -136 46 -161 57 l-46 19
+-36 297 c-50 403 -58 430 -136 452 -34 9 -671 12 -717 3z"/>
+
+<path fill="#006C4B" d="M3080 4653 c-77 -8 -195 -35 -232 -54 -21 -10 -45 -30 -54 -44 -15
+-23 -15 -27 0 -49 22 -35 111 -101 277 -207 247 -158 343 -239 384 -326 35
+-74 -5 -237 -108 -438 -40 -77 -68 -116 -122 -170 -77 -77 -194 -145 -249
+-145 -39 0 -127 48 -339 184 -194 124 -291 176 -330 176 -60 0 -105 -86 -86
+-167 28 -127 262 -492 423 -662 101 -106 236 -191 333 -211 27 -5 138 -14 248
+-20 372 -20 506 -66 689 -240 87 -83 629 -652 1291 -1355 181 -192 439 -465
+573 -607 244 -255 245 -256 281 -251 220 32 406 139 512 295 50 73 98 174 106
+222 5 32 0 42 -53 108 -113 140 -296 342 -1073 1183 -513 554 -980 1066 -1074
+1178 -117 137 -174 252 -196 394 -6 39 -15 171 -21 294 -6 123 -15 257 -20
+297 -28 207 -209 414 -464 532 -97 45 -172 66 -286 80 -78 9 -330 11 -410 3z"/>
+</g>
+</svg>
index 8292892..9ab147c 100644 (file)
@@ -1,3 +1,4 @@
+/* eslint-disable @typescript-eslint/no-unused-expressions */
 /**
  * ============LICENSE_START========================================================================
  * ONAP : ccsdk feature sdnr wt odlux
 import * as React from 'react';
 
 import Button from '@mui/material/Button';
-import TextField from '@mui/material/TextField';
 import Dialog from '@mui/material/Dialog';
 import DialogActions from '@mui/material/DialogActions';
 import DialogContent from '@mui/material/DialogContent';
 import DialogContentText from '@mui/material/DialogContentText';
 import DialogTitle from '@mui/material/DialogTitle';
+import TextField from '@mui/material/TextField';
 
-import { IDispatcher, connect, Connect } from '../../../../framework/src/flux/connect';
+import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect';
 
+import { FormControl, InputLabel, MenuItem, Select, Typography } from '@mui/material';
 import {
   addOrUpdateMaintenenceEntryAsyncActionCreator,
-  removeFromMaintenenceEntrysAsyncActionCreator
+  removeFromMaintenenceEntrysAsyncActionCreator,
 } from '../actions/maintenenceActions';
-
-import { MaintenenceEntry } from '../models/maintenenceEntryType';
-import { FormControl, InputLabel, Select, MenuItem, Typography } from '@mui/material';
+import { MaintenanceEntry } from '../models/maintenanceEntryType';
 
 export enum EditMaintenenceEntryDialogMode {
-  None = "none",
-  AddMaintenenceEntry = "addMaintenenceEntry",
-  EditMaintenenceEntry = "editMaintenenceEntry",
-  RemoveMaintenenceEntry = "removeMaintenenceEntry"
+  None = 'none',
+  AddMaintenenceEntry = 'addMaintenenceEntry',
+  EditMaintenenceEntry = 'editMaintenenceEntry',
+  RemoveMaintenenceEntry = 'removeMaintenenceEntry',
 }
 
 const mapDispatch = (dispatcher: IDispatcher) => ({
-  addOrUpdateMaintenenceEntry: (entry: MaintenenceEntry) => {
+  addOrUpdateMaintenenceEntry: (entry: MaintenanceEntry) => {
     dispatcher.dispatch(addOrUpdateMaintenenceEntryAsyncActionCreator(entry));
   },
-  removeMaintenenceEntry: (entry: MaintenenceEntry) => {
+  removeMaintenenceEntry: (entry: MaintenanceEntry) => {
     dispatcher.dispatch(removeFromMaintenenceEntrysAsyncActionCreator(entry));
   },
 });
 
 type DialogSettings = {
-  dialogTitle: string,
-  dialogDescription: string,
-  applyButtonText: string,
-  cancelButtonText: string,
-  enableMountIdEditor: boolean,
-  enableTimeEditor: boolean,
-}
+  dialogTitle: string;
+  dialogDescription: string;
+  applyButtonText: string;
+  cancelButtonText: string;
+  enableMountIdEditor: boolean;
+  enableTimeEditor: boolean;
+};
 
 const settings: { [key: string]: DialogSettings } = {
   [EditMaintenenceEntryDialogMode.None]: {
-    dialogTitle: "",
-    dialogDescription: "",
-    applyButtonText: "",
-    cancelButtonText: "",
+    dialogTitle: '',
+    dialogDescription: '',
+    applyButtonText: '',
+    cancelButtonText: '',
     enableMountIdEditor: false,
     enableTimeEditor: false,
   },
   [EditMaintenenceEntryDialogMode.AddMaintenenceEntry]: {
-    dialogTitle: "Add new maintenence entry",
-    dialogDescription: "",
-    applyButtonText: "Add",
-    cancelButtonText: "Cancel",
+    dialogTitle: 'Add new maintenence entry',
+    dialogDescription: '',
+    applyButtonText: 'Add',
+    cancelButtonText: 'Cancel',
     enableMountIdEditor: true,
     enableTimeEditor: true,
   },
   [EditMaintenenceEntryDialogMode.EditMaintenenceEntry]: {
-    dialogTitle: "Edit maintenence entry",
-    dialogDescription: "",
-    applyButtonText: "Save",
-    cancelButtonText: "Cancel",
+    dialogTitle: 'Edit maintenence entry',
+    dialogDescription: '',
+    applyButtonText: 'Save',
+    cancelButtonText: 'Cancel',
     enableMountIdEditor: false,
     enableTimeEditor: true,
   },
   [EditMaintenenceEntryDialogMode.RemoveMaintenenceEntry]: {
-    dialogTitle: "Remove maintenence entry",
-    dialogDescription: "",
-    applyButtonText: "Remove",
-    cancelButtonText: "Cancel",
+    dialogTitle: 'Remove maintenence entry',
+    dialogDescription: '',
+    applyButtonText: 'Remove',
+    cancelButtonText: 'Cancel',
     enableMountIdEditor: false,
     enableTimeEditor: false,
   },
-}
+};
 
 type EditMaintenenceEntryDIalogComponentProps = Connect<undefined, typeof mapDispatch> & {
   mode: EditMaintenenceEntryDialogMode;
-  initialMaintenenceEntry: MaintenenceEntry;
+  initialMaintenenceEntry: MaintenanceEntry;
   onClose: () => void;
 };
 
-type EditMaintenenceEntryDIalogComponentState = MaintenenceEntry & { isErrorVisible: boolean };
+type EditMaintenenceEntryDIalogComponentState = MaintenanceEntry & { isErrorVisible: boolean };
 
 class EditMaintenenceEntryDIalogComponent extends React.Component<EditMaintenenceEntryDIalogComponentProps, EditMaintenenceEntryDIalogComponentState> {
   constructor(props: EditMaintenenceEntryDIalogComponentProps) {
@@ -109,7 +109,7 @@ class EditMaintenenceEntryDIalogComponent extends React.Component<EditMaintenenc
 
     this.state = {
       ...this.props.initialMaintenenceEntry,
-      isErrorVisible: false
+      isErrorVisible: false,
     };
   }
 
@@ -122,10 +122,12 @@ class EditMaintenenceEntryDIalogComponent extends React.Component<EditMaintenenc
           <DialogContentText>
             {setting.dialogDescription}
           </DialogContentText>
-          <TextField variant="standard"  disabled={!setting.enableMountIdEditor} spellCheck={false} autoFocus margin="dense" id="name" label="Name" type="text" fullWidth value={this.state.nodeId} onChange={(event) => { this.setState({ nodeId: event.target.value }); }} />
+          <TextField variant="standard" disabled={!setting.enableMountIdEditor} spellCheck={false} autoFocus margin="dense" id="name" label="Name" type="text" fullWidth value={this.state.nodeId} onChange={(event) => { this.setState({ nodeId: event.target.value }); }} />
           {this.state.isErrorVisible && <Typography variant="body1" color="error" >Name must not be empty.</Typography>}
-          <TextField variant="standard"  disabled={!setting.enableTimeEditor} spellCheck={false} autoFocus margin="dense" id="start" label="Start (Local DateTime)" type="datetime-local" fullWidth value={this.state.start} onChange={(event) => { this.setState({ start: event.target.value }); }} />
-          <TextField variant="standard"  disabled={!setting.enableTimeEditor} spellCheck={false} autoFocus margin="dense" id="end" label="End (Local DateTime)" type="datetime-local" fullWidth value={this.state.end} onChange={(event) => { this.setState({ end: event.target.value }); }} />
+          <TextField variant="standard" disabled={!setting.enableTimeEditor} spellCheck={false} autoFocus margin="dense" id="start"
+            label="Start (Local DateTime)" type="datetime-local" fullWidth value={this.state.start} onChange={(event) => { this.setState({ start: event.target.value }); }} />
+          <TextField variant="standard" disabled={!setting.enableTimeEditor} spellCheck={false} autoFocus margin="dense" id="end"
+            label="End (Local DateTime)" type="datetime-local" fullWidth value={this.state.end} onChange={(event) => { this.setState({ end: event.target.value }); }} />
           <FormControl variant="standard" fullWidth disabled={!setting.enableTimeEditor}>
             <InputLabel htmlFor="active">Active</InputLabel>
             <Select variant="standard" value={this.state.active || false} onChange={(event) => {
@@ -143,12 +145,12 @@ class EditMaintenenceEntryDIalogComponent extends React.Component<EditMaintenenc
               this.setState({ isErrorVisible: true });
             } else {
               this.onApply({
-                _id: this.state._id || this.state.nodeId,
+                mId: this.state.mId || this.state.nodeId,
                 nodeId: this.state.nodeId,
                 description: this.state.description,
                 start: this.state.start,
                 end: this.state.end,
-                active: this.state.active
+                active: this.state.active,
               });
               this.setState({ isErrorVisible: false });
             }
@@ -164,10 +166,10 @@ class EditMaintenenceEntryDIalogComponent extends React.Component<EditMaintenenc
           }} color="secondary"> {setting.cancelButtonText} </Button>
         </DialogActions>
       </Dialog>
-    )
+    );
   }
 
-  private onApply = (entry: MaintenenceEntry) => {
+  private onApply = (entry: MaintenanceEntry) => {
     this.props.onClose && this.props.onClose();
     switch (this.props.mode) {
       case EditMaintenenceEntryDialogMode.AddMaintenenceEntry:
@@ -185,14 +187,15 @@ class EditMaintenenceEntryDIalogComponent extends React.Component<EditMaintenenc
 
   private onCancel = () => {
     this.props.onClose && this.props.onClose();
-  }
+  };
 
-  static getDerivedStateFromProps(props: EditMaintenenceEntryDIalogComponentProps, state: EditMaintenenceEntryDIalogComponentState & { _initialMaintenenceEntry: MaintenenceEntry }): EditMaintenenceEntryDIalogComponentState & { _initialMaintenenceEntry: MaintenenceEntry } {
-    if (props.initialMaintenenceEntry !== state._initialMaintenenceEntry) {
+  static getDerivedStateFromProps(props: EditMaintenenceEntryDIalogComponentProps, state: EditMaintenenceEntryDIalogComponentState & { initialMaintenenceEntrie: MaintenanceEntry }): EditMaintenenceEntryDIalogComponentState & { initialMaintenenceEntrie: MaintenanceEntry } {
+    if (props.initialMaintenenceEntry !== state.initialMaintenenceEntrie) {
+      // eslint-disable-next-line no-param-reassign
       state = {
         ...state,
         ...props.initialMaintenenceEntry,
-        _initialMaintenenceEntry: props.initialMaintenenceEntry,
+        initialMaintenenceEntrie: props.initialMaintenenceEntry,
       };
     }
     return state;
index c66b6f4..e8bd635 100644 (file)
@@ -24,78 +24,74 @@ import DialogContent from '@mui/material/DialogContent';
 import DialogContentText from '@mui/material/DialogContentText';
 import DialogTitle from '@mui/material/DialogTitle';
 
-import { IDispatcher, connect, Connect } from '../../../../framework/src/flux/connect';
+import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect';
 
-import { maintenanceEntriesReloadAction } from '../handlers/maintenenceEntriesHandler';
-import { MaintenenceEntry } from '../models/maintenenceEntryType';
+import { maintenanceEntriesReloadAction } from '../handlers/maintenanceEntriesHandler';
+import { MaintenanceEntry } from '../models/maintenanceEntryType';
 
 export enum RefreshMaintenanceEntriesDialogMode {
-  None = "none",
-  RefreshMaintenanceEntriesTable = "RefreshMaintenanceEntriesTable",
+  None = 'none',
+  RefreshMaintenanceEntriesTable = 'RefreshMaintenanceEntriesTable',
 }
 
 const mapDispatch = (dispatcher: IDispatcher) => ({
-  refreshMaintenanceEntries: () => dispatcher.dispatch(maintenanceEntriesReloadAction)
+  refreshMaintenanceEntries: () => dispatcher.dispatch(maintenanceEntriesReloadAction),
 });
 
 type DialogSettings = {
-  dialogTitle: string,
-  dialogDescription: string,
-  applyButtonText: string,
-  cancelButtonText: string,
-  enableMountIdEditor: boolean,
-  enableUsernameEditor: boolean,
-  enableExtendedEditor: boolean,
-}
+  dialogTitle: string;
+  dialogDescription: string;
+  applyButtonText: string;
+  cancelButtonText: string;
+  enableMountIdEditor: boolean;
+  enableUsernameEditor: boolean;
+  enableExtendedEditor: boolean;
+};
 
 const settings: { [key: string]: DialogSettings } = {
   [RefreshMaintenanceEntriesDialogMode.None]: {
-    dialogTitle: "",
-    dialogDescription: "",
-    applyButtonText: "",
-    cancelButtonText: "",
+    dialogTitle: '',
+    dialogDescription: '',
+    applyButtonText: '',
+    cancelButtonText: '',
     enableMountIdEditor: false,
     enableUsernameEditor: false,
     enableExtendedEditor: false,
   },
   [RefreshMaintenanceEntriesDialogMode.RefreshMaintenanceEntriesTable]: {
-    dialogTitle: "Do you want to refresh Maintenance Entries?",
-    dialogDescription: "",
-    applyButtonText: "Yes",
-    cancelButtonText: "Cancel",
+    dialogTitle: 'Do you want to refresh Maintenance Entries?',
+    dialogDescription: '',
+    applyButtonText: 'Yes',
+    cancelButtonText: 'Cancel',
     enableMountIdEditor: true,
     enableUsernameEditor: true,
     enableExtendedEditor: true,
-  }
-}
+  },
+};
 
 type RefreshMaintenanceEntriesDialogComponentProps = Connect<undefined, typeof mapDispatch> & {
   mode: RefreshMaintenanceEntriesDialogMode;
   onClose: () => void;
 };
 
-type RefreshMaintenanceEntriesDialogComponentState = MaintenenceEntry & { isNameValid: boolean, isHostSet: boolean };
+type RefreshMaintenanceEntriesDialogComponentState = MaintenanceEntry & { isNameValid: boolean; isHostSet: boolean };
 
 class RefreshMaintenanceEntriesDialogComponent extends React.Component<RefreshMaintenanceEntriesDialogComponentProps, RefreshMaintenanceEntriesDialogComponentState> {
-  constructor(props: RefreshMaintenanceEntriesDialogComponentProps) {
-    super(props);
-  }
-
   render(): JSX.Element {
     const setting = settings[this.props.mode];
     return (
       <Dialog open={this.props.mode !== RefreshMaintenanceEntriesDialogMode.None}>
-        <DialogTitle id="form-dialog-title" aria-label={`${setting.dialogTitle.replace(/ /g, "-").toLowerCase()}-dialog`}>{setting.dialogTitle}</DialogTitle>
+        <DialogTitle id="form-dialog-title" aria-label={`${setting.dialogTitle.replace(/ /g, '-').toLowerCase()}-dialog`}>{setting.dialogTitle}</DialogTitle>
         <DialogContent>
           <DialogContentText>
             {setting.dialogDescription}
           </DialogContentText>
         </DialogContent>
         <DialogActions>
-          <Button aria-label="dialog-confirm-button" onClick={(event) => {
+          <Button aria-label="dialog-confirm-button" onClick={() => {
             this.onRefresh();
           }} color="inherit" > {setting.applyButtonText} </Button>
-          <Button aria-label="dialog-cancel-button" onClick={(event) => {
+          <Button aria-label="dialog-cancel-button" onClick={() => {
             this.onCancel();
           }} color="secondary"> {setting.cancelButtonText} </Button>
         </DialogActions>
@@ -110,7 +106,7 @@ class RefreshMaintenanceEntriesDialogComponent extends React.Component<RefreshMa
 
   private onCancel = () => {
     this.props.onClose();
-  }
+  };
 }
 
 export const RefreshMaintenanceEntriesDialog = connect(undefined, mapDispatch)(RefreshMaintenanceEntriesDialogComponent);
index 71b4bf7..ced7f21 100644 (file)
 
 import { combineActionHandler } from '../../../../framework/src/flux/middleware';
 
-import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
-
-import { IMaintenanceEntriesState, maintenanceEntriesActionHandler } from './maintenenceEntriesHandler';
+import { IMaintenanceEntriesState, maintenanceEntriesActionHandler } from './maintenanceEntriesHandler';
 
 export interface IMaintenanceAppStoreState {
-  maintenenceEntries : IMaintenanceEntriesState
+  maintenanceEntries : IMaintenanceEntriesState;
 }
 
 declare module '../../../../framework/src/store/applicationStore' {
   interface IApplicationStoreState {
-    maintenance: IMaintenanceAppStoreState
+    maintenance: IMaintenanceAppStoreState;
   }
 }
 
 const actionHandlers = {
-  maintenenceEntries: maintenanceEntriesActionHandler
+  maintenanceEntries: maintenanceEntriesActionHandler,
 };
 
 export const maintenanceAppRootHandler = combineActionHandler<IMaintenanceAppStoreState>(actionHandlers);
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import { createExternal,IExternalTableState } from '../../../../framework/src/components/material-table/utilities';
+import { createExternal, IExternalTableState } from '../../../../framework/src/components/material-table/utilities';
 import { createSearchDataHandler } from '../../../../framework/src/utilities/elasticSearch';
 
-import { MaintenenceEntry } from '../models/maintenenceEntryType';
-export interface IMaintenanceEntriesState extends IExternalTableState<MaintenenceEntry> { }
+import { MaintenanceEntry } from '../models/maintenanceEntryType';
+export interface IMaintenanceEntriesState extends IExternalTableState<MaintenanceEntry> { }
 
-// create eleactic search material data fetch handler
-const maintenanceEntriesSearchHandler = createSearchDataHandler<MaintenenceEntry>('maintenance');
+// create elastic search material data fetch handler
+const maintenanceEntriesSearchHandler = createSearchDataHandler<MaintenanceEntry>('maintenance');
 
 export const {
   actionHandler: maintenanceEntriesActionHandler,
@@ -31,5 +31,5 @@ export const {
   reloadAction: maintenanceEntriesReloadAction,
 
   // set value action, to change a value
-} = createExternal<MaintenenceEntry>(maintenanceEntriesSearchHandler, appState => appState.maintenance.maintenenceEntries);
+} = createExternal<MaintenanceEntry>(maintenanceEntriesSearchHandler, appState => appState.maintenance.maintenanceEntries);
 
 /** Represents the elestic search db type for maintenence enrties */
 
 
-export const spoofSymbol = Symbol("Spoof");
+export const spoofSymbol = Symbol('Spoof');
 
 /** Represents the type for an maintenence entry. */
-export type MaintenenceEntry = {
-  _id: string;
+export type MaintenanceEntry = {
+  mId: string;
   nodeId: string;
-  description?: string,
-  end: string,
-  start: string
-  active: boolean
+  description?: string;
+  end: string;
+  start: string;
+  active: boolean;
   [spoofSymbol]?: boolean;
-}
+};
 
index e6ab977..0f686cb 100644 (file)
  */
 // app configuration and main entry point for the app
 
-import * as React from "react";
-
-import { faLock } from '@fortawesome/free-solid-svg-icons';  // select app icon
+import React, { FC } from 'react';
 
 import applicationManager from '../../../framework/src/services/applicationManager';
 
 import { maintenanceAppRootHandler } from './handlers/maintenanceAppRootHandler';
 
-import MaintenenceView from "./views/maintenenceView";
+import { MaintenanceView } from './views/maintenanceView';
+
+const appIcon = require('./assets/icons/maintenanceAppIcon.svg');  // select app icon
 
-const App : React.SFC = (props) => {
-  return <MaintenenceView />
+const App : FC = () => {
+  return <MaintenanceView />;
 };
 
 export function register() {
   applicationManager.registerApplication({
-    name: "maintenance",
-    icon: faLock,
+    name: 'maintenance',
+    icon: appIcon,
     rootComponent: App,
     rootActionHandler: maintenanceAppRootHandler,
-    menuEntry: "Maintenance"
+    menuEntry: 'Maintenance',
   });
 }
 
index 613b478..5fdccc3 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
+import { DeleteResponse, PostResponse } from '../../../../framework/src/models/elasticSearch';
 import { requestRest } from '../../../../framework/src/services/restService';
-import { Result, HitEntry, PostResponse, DeleteResponse } from '../../../../framework/src/models/elasticSearch';
-
-import { MaintenenceEntry } from '../models/maintenenceEntryType';
-import { convertToLocaleString, convertToGMTString, convertToISODateString } from '../utils/timeUtils';
 import { convertPropertyNames, replaceUpperCase } from '../../../../framework/src/utilities/yangHelper';
+import { MaintenanceEntry } from '../models/maintenanceEntryType';
+
+import { convertToISODateString } from '../utils/timeUtils';
 
 
-export const maintenenceEntryDatabasePath = "mwtn/maintenancemode";
+export const maintenenceEntryDatabasePath = 'mwtn/maintenancemode';
 
 /**
  * Represents a web api accessor service for all maintenence entries related actions.
@@ -33,37 +33,37 @@ class MaintenenceService {
   /**
   * Adds or updates one maintenence entry to the backend.
   */
-  public async writeMaintenenceEntry(maintenenceEntry: MaintenenceEntry): Promise<PostResponse | null> {
-    const path = `/rests/operations/data-provider:create-maintenance`;
+  public async writeMaintenenceEntry(maintenenceEntry: MaintenanceEntry): Promise<PostResponse | null> {
+    const path = '/rests/operations/data-provider:create-maintenance';
 
     const query = {
-      "id": maintenenceEntry._id,
-      "node-id": maintenenceEntry.nodeId,
-      "active": maintenenceEntry.active,
-      "description": maintenenceEntry.description,
-      "end": convertToISODateString(maintenenceEntry.end),
-      "start": convertToISODateString(maintenenceEntry.start)
+      'id': maintenenceEntry.mId,
+      'node-id': maintenenceEntry.nodeId,
+      'active': maintenenceEntry.active,
+      'description': maintenenceEntry.description,
+      'end': convertToISODateString(maintenenceEntry.end),
+      'start': convertToISODateString(maintenenceEntry.start),
     };
 
-    const result = await requestRest<PostResponse>(path, { method: "POST", body: JSON.stringify(convertPropertyNames({ "data-provider:input": query }, replaceUpperCase)) });
+    const result = await requestRest<PostResponse>(path, { method: 'POST', body: JSON.stringify(convertPropertyNames({ 'data-provider:input': query }, replaceUpperCase)) });
     return result || null;
   }
 
   /**
   * Deletes one maintenence entry by its mountId from the backend.
   */
-  public async deleteMaintenenceEntry(maintenenceEntry: MaintenenceEntry): Promise<(DeleteResponse) | null> {
-    const path = `/rests/operations/data-provider:delete-maintenance`;
+  public async deleteMaintenenceEntry(maintenenceEntry: MaintenanceEntry): Promise<(DeleteResponse) | null> {
+    const path = '/rests/operations/data-provider:delete-maintenance';
 
     const query = {
-      "id": maintenenceEntry._id,
-      "node-id": maintenenceEntry.nodeId,
-      "active": maintenenceEntry.active,
-      "description": maintenenceEntry.description,
-      "end": convertToISODateString(maintenenceEntry.end),
-      "start": convertToISODateString(maintenenceEntry.start)
+      'id': maintenenceEntry.mId,
+      'node-id': maintenenceEntry.nodeId,
+      'active': maintenenceEntry.active,
+      'description': maintenenceEntry.description,
+      'end': convertToISODateString(maintenenceEntry.end),
+      'start': convertToISODateString(maintenenceEntry.start),
     };
-    const result = await requestRest<DeleteResponse>(path, { method: "POST", body: JSON.stringify(convertPropertyNames({ "data-provider:input": query }, replaceUpperCase)) });
+    const result = await requestRest<DeleteResponse>(path, { method: 'POST', body: JSON.stringify(convertPropertyNames({ 'data-provider:input': query }, replaceUpperCase)) });
     return result || null;
   }
 }
index 676be1a..0fde5fc 100644 (file)
@@ -27,7 +27,7 @@ export function convertToGMTString(dateString: string): string {
     '+00:00';
 }
 
-export function convertToLocaleString(rawDate: string| number): string {
+export function convertToLocaleString(rawDate: string | number): string {
   const date = new Date(rawDate);
   const pad = (n: number) => (n < 10) ? '0' + n : n;
 
@@ -38,7 +38,7 @@ export function convertToLocaleString(rawDate: string| number): string {
     ':' + pad(date.getMinutes());
 }
 
-export function convertToISODateString(rawDate: string| number): string {
+export function convertToISODateString(rawDate: string | number): string {
   const date = new Date(rawDate);
   const displayDate = date.toISOString();
   return displayDate.replace(/\.[0-9]{2}/, '.');
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
-
-import { Theme, Tooltip } from '@mui/material';
-
-import { WithStyles } from '@mui/styles';
-import createStyles from '@mui/styles/createStyles';
-import withStyles from '@mui/styles/withStyles';
+import React from 'react';
 
 import { faBan } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-
 import AddIcon from '@mui/icons-material/Add';
 import EditIcon from '@mui/icons-material/Edit';
-import RemoveIcon from '@mui/icons-material/RemoveCircleOutline';
 import Refresh from '@mui/icons-material/Refresh';
-import { MenuItem, Divider, Typography } from '@mui/material';
+import RemoveIcon from '@mui/icons-material/RemoveCircleOutline';
+import { Divider, MenuItem, Theme, Typography } from '@mui/material';
+import { WithStyles } from '@mui/styles';
+import createStyles from '@mui/styles/createStyles';
+import withStyles from '@mui/styles/withStyles';
 
-import connect, { IDispatcher, Connect } from '../../../../framework/src/flux/connect';
-import MaterialTable, { MaterialTableCtorType, ColumnType } from '../../../../framework/src/components/material-table';
+import MaterialTable, { ColumnType, MaterialTableCtorType } from '../../../../framework/src/components/material-table';
+import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect';
 import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
 
-import { MaintenenceEntry, spoofSymbol } from '../models/maintenenceEntryType';
-
 import EditMaintenenceEntryDialog, { EditMaintenenceEntryDialogMode } from '../components/editMaintenenceEntryDialog';
 import RefreshMaintenanceEntriesDialog, { RefreshMaintenanceEntriesDialogMode } from '../components/refreshMaintenanceEntries';
+import { createmaintenanceEntriesActions, createmaintenanceEntriesProperties, maintenanceEntriesReloadAction } from '../handlers/maintenanceEntriesHandler';
+import { MaintenanceEntry } from '../models/maintenanceEntryType';
 import { convertToLocaleString } from '../utils/timeUtils';
-import { createmaintenanceEntriesActions, createmaintenanceEntriesProperties, maintenanceEntriesReloadAction } from '../handlers/maintenenceEntriesHandler';
 
 const styles = (theme: Theme) => createStyles({
   button: {
     margin: 0,
-    padding: "6px 6px",
-    minWidth: 'unset'
+    padding: '6px 6px',
+    minWidth: 'unset',
   },
   spacer: {
     marginLeft: theme.spacing(1),
     marginRight: theme.spacing(1),
-    display: "inline"
-  }
+    display: 'inline',
+  },
 });
 
-const MaintenenceEntriesTable = MaterialTable as MaterialTableCtorType<MaintenenceEntry>;
+const MaintenanceEntriesTable = MaterialTable as MaterialTableCtorType<MaintenanceEntry>;
 
 const mapProps = (state: IApplicationStoreState) => ({
-  maintenanceEntriesProperties: createmaintenanceEntriesProperties(state)
+  maintenanceEntriesProperties: createmaintenanceEntriesProperties(state),
 });
 
 const mapDispatcher = (dispatcher: IDispatcher) => ({
   maintenanceEntriesActions: createmaintenanceEntriesActions(dispatcher.dispatch),
   onLoadMaintenanceEntries: async () => {
-    await dispatcher.dispatch(maintenanceEntriesReloadAction)
-  }
+    await dispatcher.dispatch(maintenanceEntriesReloadAction);
+  },
 });
 
-const emptyMaintenenceEntry: MaintenenceEntry = {
-  _id: '',
+const emptyMaintenenceEntry: MaintenanceEntry = {
+  mId: '',
   nodeId: '',
   description: '',
   start: convertToLocaleString(new Date().valueOf()),
@@ -78,44 +73,41 @@ const emptyMaintenenceEntry: MaintenenceEntry = {
   active: false,
 };
 
-type MaintenenceViewComponentProps = Connect<typeof mapProps, typeof mapDispatcher> & WithStyles<typeof styles> & {
-
-}
+type MaintenanceViewComponentProps = Connect<typeof mapProps, typeof mapDispatcher> & WithStyles<typeof styles> & {};
 
 type MaintenenceViewComponentState = {
-  maintenenceEntryToEdit: MaintenenceEntry;
-  maintenenceEntryEditorMode: EditMaintenenceEntryDialogMode;
+  maintenenceEntryToEdit: MaintenanceEntry;
+  maintenanceEntryEditorMode: EditMaintenenceEntryDialogMode;
   refreshMaintenenceEntriesEditorMode: RefreshMaintenanceEntriesDialogMode;
 };
 
 let initialSorted = false;
 
-class MaintenenceViewComponent extends React.Component<MaintenenceViewComponentProps, MaintenenceViewComponentState> {
+class MaintenenceViewComponent extends React.Component<MaintenanceViewComponentProps, MaintenenceViewComponentState> {
 
-  constructor(props: MaintenenceViewComponentProps) {
+  constructor(props: MaintenanceViewComponentProps) {
     super(props);
 
     this.state = {
       maintenenceEntryToEdit: emptyMaintenenceEntry,
-      maintenenceEntryEditorMode: EditMaintenenceEntryDialogMode.None,
-      refreshMaintenenceEntriesEditorMode: RefreshMaintenanceEntriesDialogMode.None
+      maintenanceEntryEditorMode: EditMaintenenceEntryDialogMode.None,
+      refreshMaintenenceEntriesEditorMode: RefreshMaintenanceEntriesDialogMode.None,
     };
 
   }
 
-  getContextMenu(rowData: MaintenenceEntry): JSX.Element[] {
+  getContextMenu(rowData: MaintenanceEntry): JSX.Element[] {
     let buttonArray = [
-      <MenuItem aria-label={"1hr-from-now"} onClick={event => this.onOpenPlus1hEditMaintenenceEntryDialog(event, rowData)}><Typography>+1h</Typography></MenuItem>,
-      <MenuItem aria-label={"8hr-from-now"} onClick={event => this.onOpenPlus8hEditMaintenenceEntryDialog(event, rowData)}><Typography>+8h</Typography></MenuItem>,
+      <MenuItem aria-label={'1hr-from-now'} onClick={event => this.onOpenPlus1hEditMaintenenceEntryDialog(event, rowData)}><Typography>+1h</Typography></MenuItem>,
+      <MenuItem aria-label={'8hr-from-now'} onClick={event => this.onOpenPlus8hEditMaintenenceEntryDialog(event, rowData)}><Typography>+8h</Typography></MenuItem>,
       <Divider />,
-      <MenuItem aria-label={"edit"} onClick={event => this.onOpenEditMaintenenceEntryDialog(event, rowData)}><EditIcon /><Typography>Edit</Typography></MenuItem>,
-      <MenuItem aria-label={"remove"} onClick={event => this.onOpenRemoveMaintenenceEntryDialog(event, rowData)}><RemoveIcon /><Typography>Remove</Typography></MenuItem>
+      <MenuItem aria-label={'edit'} onClick={event => this.onOpenEditMaintenenceEntryDialog(event, rowData)}><EditIcon /><Typography>Edit</Typography></MenuItem>,
+      <MenuItem aria-label={'remove'} onClick={event => this.onOpenRemoveMaintenenceEntryDialog(event, rowData)}><RemoveIcon /><Typography>Remove</Typography></MenuItem>,
     ];
     return buttonArray;
   }
 
   render() {
-    const { classes } = this.props;
     const addMaintenenceEntryAction = {
       icon: AddIcon, tooltip: 'Add', ariaLabel:'add-element', onClick: () => {
         const startTime = (new Date().valueOf());
@@ -126,39 +118,39 @@ class MaintenenceViewComponent extends React.Component<MaintenenceViewComponentP
             start: convertToLocaleString(startTime),
             end: convertToLocaleString(endTime),
           },
-          maintenenceEntryEditorMode: EditMaintenenceEntryDialogMode.AddMaintenenceEntry
+          maintenanceEntryEditorMode: EditMaintenenceEntryDialogMode.AddMaintenenceEntry,
         });
-      }
+      },
     };
 
     const refreshMaintenanceEntriesAction = {
       icon: Refresh, tooltip: 'Refresh Maintenance Entries', ariaLabel: 'refresh', onClick: () => {
         this.setState({
-          refreshMaintenenceEntriesEditorMode: RefreshMaintenanceEntriesDialogMode.RefreshMaintenanceEntriesTable
+          refreshMaintenenceEntriesEditorMode: RefreshMaintenanceEntriesDialogMode.RefreshMaintenanceEntriesTable,
         });
-      }
+      },
     };
 
     const now = new Date().valueOf();
     return (
       <>
-        <MaintenenceEntriesTable stickyHeader tableId="maintenance-table" title={"Maintenance"} customActionButtons={[refreshMaintenanceEntriesAction, addMaintenenceEntryAction]} columns={
+        <MaintenanceEntriesTable stickyHeader tableId="maintenance-table" title={'Maintenance'} customActionButtons={[refreshMaintenanceEntriesAction, addMaintenenceEntryAction]} columns={
           [
-            { property: "nodeId", title: "Node Name", type: ColumnType.text },
+            { property: 'nodeId', title: 'Node Name', type: ColumnType.text },
             {
-              property: "notifications", title: "Notification", width: 50, align: "center", type: ColumnType.custom, customControl: ({ rowData }) => (
+              property: 'notifications', title: 'Notification', width: 50, align: 'center', type: ColumnType.custom, customControl: ({ rowData }) => (
                 rowData.active && (Date.parse(rowData.start).valueOf() <= now) && (Date.parse(rowData.end).valueOf() >= now) && <FontAwesomeIcon icon={faBan} /> || null
-              )
+              ),
             },
-            { property: "active", title: "Activation State", type: ColumnType.boolean, labels: { "true": "active", "false": "not active" }, },
-            { property: "start", title: "Start Date (UTC)", type: ColumnType.text },
-            { property: "end", title: "End Date (UTC)", type: ColumnType.text }
+            { property: 'active', title: 'Activation State', type: ColumnType.boolean, labels: { 'true': 'active', 'false': 'not active' } },
+            { property: 'start', title: 'Start Date (UTC)', type: ColumnType.text },
+            { property: 'end', title: 'End Date (UTC)', type: ColumnType.text },
           ]
-        } idProperty={'_id'}{...this.props.maintenanceEntriesActions} {...this.props.maintenanceEntriesProperties} asynchronus createContextMenu={rowData => {
+        } idProperty={'mId'}{...this.props.maintenanceEntriesActions} {...this.props.maintenanceEntriesProperties} asynchronus createContextMenu={rowData => {
           return this.getContextMenu(rowData);
         }} >
-        </MaintenenceEntriesTable>
-        <EditMaintenenceEntryDialog initialMaintenenceEntry={this.state.maintenenceEntryToEdit} mode={this.state.maintenenceEntryEditorMode}
+        </MaintenanceEntriesTable>
+        <EditMaintenenceEntryDialog initialMaintenenceEntry={this.state.maintenenceEntryToEdit} mode={this.state.maintenanceEntryEditorMode}
           onClose={this.onCloseEditMaintenenceEntryDialog} />
         <RefreshMaintenanceEntriesDialog mode={this.state.refreshMaintenenceEntriesEditorMode}
           onClose={this.onCloseRefreshMaintenenceEntryDialog} />
@@ -170,7 +162,7 @@ class MaintenenceViewComponent extends React.Component<MaintenenceViewComponentP
 
     if (!initialSorted) {
       initialSorted = true;
-      this.props.maintenanceEntriesActions.onHandleRequestSort("node-id");
+      this.props.maintenanceEntriesActions.onHandleRequestSort('node-id');
     } else {
       this.props.onLoadMaintenanceEntries();
     }
@@ -178,7 +170,7 @@ class MaintenenceViewComponent extends React.Component<MaintenenceViewComponentP
 
   }
 
-  private onOpenPlus1hEditMaintenenceEntryDialog = (event: React.MouseEvent<HTMLElement>, entry: MaintenenceEntry) => {
+  private onOpenPlus1hEditMaintenenceEntryDialog = (event: React.MouseEvent<HTMLElement>, entry: MaintenanceEntry) => {
     // event.preventDefault();
     // event.stopPropagation();
     const startTime = (new Date().valueOf());
@@ -189,11 +181,11 @@ class MaintenenceViewComponent extends React.Component<MaintenenceViewComponentP
         start: convertToLocaleString(startTime),
         end: convertToLocaleString(endTime),
       },
-      maintenenceEntryEditorMode: EditMaintenenceEntryDialogMode.EditMaintenenceEntry
+      maintenanceEntryEditorMode: EditMaintenenceEntryDialogMode.EditMaintenenceEntry,
     });
-  }
+  };
 
-  private onOpenPlus8hEditMaintenenceEntryDialog = (event: React.MouseEvent<HTMLElement>, entry: MaintenenceEntry) => {
+  private onOpenPlus8hEditMaintenenceEntryDialog = (event: React.MouseEvent<HTMLElement>, entry: MaintenanceEntry) => {
     // event.preventDefault();
     // event.stopPropagation();
     const startTime = (new Date().valueOf());
@@ -204,11 +196,11 @@ class MaintenenceViewComponent extends React.Component<MaintenenceViewComponentP
         start: convertToLocaleString(startTime),
         end: convertToLocaleString(endTime),
       },
-      maintenenceEntryEditorMode: EditMaintenenceEntryDialogMode.EditMaintenenceEntry
+      maintenanceEntryEditorMode: EditMaintenenceEntryDialogMode.EditMaintenenceEntry,
     });
-  }
+  };
 
-  private onOpenEditMaintenenceEntryDialog = (event: React.MouseEvent<HTMLElement>, entry: MaintenenceEntry) => {
+  private onOpenEditMaintenenceEntryDialog = (event: React.MouseEvent<HTMLElement>, entry: MaintenanceEntry) => {
     // event.preventDefault();
     // event.stopPropagation();
     const startTime = (new Date().valueOf());
@@ -216,13 +208,13 @@ class MaintenenceViewComponent extends React.Component<MaintenenceViewComponentP
     this.setState({
       maintenenceEntryToEdit: {
         ...entry,
-        ...(entry.start && endTime ? { start: convertToLocaleString(entry.start), end: convertToLocaleString(entry.end) } : { start: convertToLocaleString(startTime), end: convertToLocaleString(endTime) })
+        ...(entry.start && endTime ? { start: convertToLocaleString(entry.start), end: convertToLocaleString(entry.end) } : { start: convertToLocaleString(startTime), end: convertToLocaleString(endTime) }),
       },
-      maintenenceEntryEditorMode: EditMaintenenceEntryDialogMode.EditMaintenenceEntry
+      maintenanceEntryEditorMode: EditMaintenenceEntryDialogMode.EditMaintenenceEntry,
     });
-  }
+  };
 
-  private onOpenRemoveMaintenenceEntryDialog = (event: React.MouseEvent<HTMLElement>, entry: MaintenenceEntry) => {
+  private onOpenRemoveMaintenenceEntryDialog = (event: React.MouseEvent<HTMLElement>, entry: MaintenanceEntry) => {
     // event.preventDefault();
     // event.stopPropagation();
     const startTime = (new Date().valueOf());
@@ -230,25 +222,25 @@ class MaintenenceViewComponent extends React.Component<MaintenenceViewComponentP
     this.setState({
       maintenenceEntryToEdit: {
         ...entry,
-        ...(entry.start && endTime ? { start: convertToLocaleString(entry.start), end: convertToLocaleString(entry.end) } : { start: convertToLocaleString(startTime), end: convertToLocaleString(endTime) })
+        ...(entry.start && endTime ? { start: convertToLocaleString(entry.start), end: convertToLocaleString(entry.end) } : { start: convertToLocaleString(startTime), end: convertToLocaleString(endTime) }),
       },
-      maintenenceEntryEditorMode: EditMaintenenceEntryDialogMode.RemoveMaintenenceEntry
+      maintenanceEntryEditorMode: EditMaintenenceEntryDialogMode.RemoveMaintenenceEntry,
     });
-  }
+  };
 
   private onCloseEditMaintenenceEntryDialog = () => {
     this.setState({
       maintenenceEntryToEdit: emptyMaintenenceEntry,
-      maintenenceEntryEditorMode: EditMaintenenceEntryDialogMode.None,
+      maintenanceEntryEditorMode: EditMaintenenceEntryDialogMode.None,
     });
-  }
+  };
 
   private onCloseRefreshMaintenenceEntryDialog = () => {
     this.setState({
       refreshMaintenenceEntriesEditorMode: RefreshMaintenanceEntriesDialogMode.None,
     });
-  }
+  };
 }
 
-export const MaintenenceView = withStyles(styles)(connect(mapProps, mapDispatcher)(MaintenenceViewComponent));
-export default MaintenenceView;
+export const MaintenanceView = withStyles(styles)(connect(mapProps, mapDispatcher)(MaintenenceViewComponent));
+
index a66b5d8..ca65092 100644 (file)
@@ -4,7 +4,7 @@
     "outDir": "./dist",
     "sourceMap": true,
     "forceConsistentCasingInFileNames": true,
-    "allowSyntheticDefaultImports": false,
+    "allowSyntheticDefaultImports": true,
     "allowUnreachableCode": false,
     "allowUnusedLabels": false,
     "noFallthroughCasesInSwitch": true,
index da5cf58..845fc43 100644 (file)
@@ -57,6 +57,16 @@ module.exports = (env) => {
         use: [{
           loader: "babel-loader"
         }]
+      }, {
+        //don't minify images
+        test: /\.(png|gif|jpg|svg)$/,
+        use: [{
+          loader: 'url-loader',
+          options: {
+            limit: 10,
+            name: './images/[name].[ext]'
+          }
+        }]
       }]
     },
 
index 2f8a147..2406c6c 100644 (file)
@@ -19,6 +19,7 @@
   ~ ============LICENSE_END=======================================================
   ~
   -->
+
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
                         <!-- optional: default phase is "generate-resources" -->
                         <phase>initialize</phase>
                         <configuration>
-                            <nodeVersion>v12.13.0</nodeVersion>
+                            <nodeVersion>v12.22.0</nodeVersion>
                             <yarnVersion>v1.22.10</yarnVersion>
                         </configuration>
                     </execution>
diff --git a/sdnr/wt/odlux/apps/mediatorApp/src/assets/icons/mediatorAppIcon.svg b/sdnr/wt/odlux/apps/mediatorApp/src/assets/icons/mediatorAppIcon.svg
new file mode 100644 (file)
index 0000000..b819aa6
--- /dev/null
@@ -0,0 +1,49 @@
+<!-- highstreet technologies GmbH colour scheme 
+       Grey    #565656
+       LBlue #36A9E1
+       DBlue #246DA2
+       Green #003F2C / #006C4B
+       Yellw #C8D400
+       Red     #D81036
+-->
+
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 980 980">
+
+<g transform="translate(0.000000,890.000000) scale(0.100000,-0.100000)">
+
+<path fill="#36A9E1" d="M4704 7791 c-314 -180 -1992 -1183 -1993 -1191 -2 -12 2183 -1311
+2198 -1307 20 5 1595 947 1856 1109 198 123 295 193 295 210 0 7 -22 26 -48
+42 -352 217 -2060 1224 -2103 1241 -9 3 -94 -40 -205 -104z"/>
+
+<path fill="#36A9E1" d="M2530 5147 l0 -1144 1098 -731 1097 -732 3 1217 c2 966 -1 1220 -10
+1229 -19 17 -2169 1304 -2179 1304 -5 0 -9 -475 -9 -1143z"/>
+
+<path fill="#36A9E1" d="M6163 5638 l-1093 -653 0 -1232 c0 -678 2 -1233 4 -1233 8 0 2161
+1293 2179 1308 16 14 17 62 16 761 -1 410 -5 961 -8 1224 l-6 477 -1092 -652z"/>
+
+<path fill="#565656" d="M7598 4488 c-3 -626 -6 -800 -17 -825 -16 -39 -136 -122 -486 -337
+-132 -81 -249 -154 -260 -162 -20 -15 -19 -16 20 -50 44 -39 414 -264 517
+-315 l66 -32 109 63 c107 62 414 243 838 493 383 226 1199 715 1219 730 19 14
+16 17 -50 59 -97 62 -1941 1168 -1948 1168 -3 0 -6 -357 -8 -792z"/>
+
+<path fill="#565656" d="M1280 4736 c-1071 -644 -1095 -659 -1093 -671 0 -5 492 -303 1092
+-661 l1091 -653 400 197 c219 108 399 199 399 202 0 3 -102 73 -226 155 -333
+220 -653 442 -691 479 -24 24 -35 45 -42 86 -6 30 -13 358 -16 728 -5 477 -10
+672 -18 671 -6 0 -409 -240 -896 -533z"/>
+
+<path fill="#565656" d="M0 2612 l0 -1149 872 -578 c1051 -697 1292 -855 1307 -855 8 0 11
+333 11 1213 l0 1212 -1063 637 c-584 350 -1077 643 -1094 652 l-33 17 0 -1149z"/>
+
+<path fill="#565656" d="M9380 3514 c-217 -129 -704 -420 -1082 -647 l-688 -412 0 -1228 0
+-1228 28 15 c56 29 810 476 1692 1003 l465 278 3 1228 c2 978 0 1227 -10 1227
+-7 0 -191 -106 -408 -236z"/>
+
+<path fill="#565656" d="M3019 2685 l-484 -243 2 -1124 c2 -1073 6 -1308 22 -1308 11 0 1943
+1147 2134 1267 l37 23 0 413 0 412 -378 250 c-331 219 -807 530 -838 548 -6 3
+-229 -103 -495 -238z"/>
+
+<path fill="#565656" d="M6249 2821 c-101 -60 -407 -244 -679 -408 l-495 -299 0 -409 0 -410
+440 -263 c1265 -755 1717 -1022 1733 -1022 9 0 12 292 12 1218 0 669 -4 1222
+-8 1228 -13 19 -788 474 -808 474 -6 0 -93 -49 -195 -109z"/>
+</g>
+</svg>
index 2b91079..f8691ab 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
-import * as React from 'react'
+import React from 'react'
 import { Dialog, DialogTitle, DialogContent, DialogActions, TextField, DialogContentText, Checkbox, Button, FormControlLabel, FormGroup } from '@mui/material';
 import { IApplicationState } from '../../../../framework/src/handlers/applicationStateHandler';
 import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
-import connect, { Connect } from '../../../../framework/src/flux/connect';
-import { MediatorConfigResponse } from 'models/mediatorServer';
+import { connect, Connect } from '../../../../framework/src/flux/connect';
+import { MediatorConfigResponse } from '../models/mediatorServer';
 import { Panel } from '../../../../framework/src/components/material-ui/panel';
 
 export enum MediatorInfoDialogMode {
index 5ffd012..1c30cfc 100644 (file)
  */
 // app configuration and main entry point for the app
 
-import * as React from "react";
+import React from "react";
 import { withRouter, RouteComponentProps, Route, Switch, Redirect } from 'react-router-dom';
 
-import { faGlobe } from '@fortawesome/free-solid-svg-icons';  // select app icon
-
 import applicationManager from '../../../framework/src/services/applicationManager';
-import { IApplicationStoreState } from "../../../framework/src/store/applicationStore";
-import connect, { Connect, IDispatcher } from '../../../framework/src/flux/connect';
+
+import { connect, Connect, IDispatcher } from '../../../framework/src/flux/connect';
 
 import { mediatorAppRootHandler } from './handlers/mediatorAppRootHandler';
 import { avaliableMediatorServersReloadAction } from "./handlers/avaliableMediatorServersHandler";
@@ -33,6 +31,8 @@ import { MediatorApplication } from "./views/mediatorApplication";
 import { MediatorServerSelection } from "./views/mediatorServerSelection";
 import { initializeMediatorServerAsyncActionCreator } from "./actions/mediatorServerActions";
 
+const appIcon = require('./assets/icons/mediatorAppIcon.svg');  // select app icon
+
 let currentMediatorServerId: string | undefined = undefined;
 
 const mapDisp = (dispatcher: IDispatcher) => ({
@@ -70,13 +70,13 @@ const FinalApp = withRouter(connect()(App));
 export function register() {
   const applicationApi = applicationManager.registerApplication({
     name: "mediator",
-    icon: faGlobe,
+    icon: appIcon,
     rootComponent: FinalApp,
     rootActionHandler: mediatorAppRootHandler,
     menuEntry: "Mediator"
   });
 
-  // prefetch all avaliable mediator servers
+  // prefetch all available mediator servers
   applicationApi.applicationStoreInitialized.then(applicationStore => {
     applicationStore.dispatch(avaliableMediatorServersReloadAction)
   });
index 55d9b40..03ce453 100644 (file)
@@ -15,7 +15,7 @@
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
+import React from 'react';
 import { Theme, Tooltip } from '@mui/material';
 
 import { WithStyles } from '@mui/styles';
@@ -33,7 +33,7 @@ import StopIcon from '@mui/icons-material/Stop';
 import CircularProgress from '@mui/material/CircularProgress'
 
 import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
-import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect';
+import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect';
 import MaterialTable, { MaterialTableCtorType, ColumnType } from '../../../../framework/src/components/material-table';
 
 import { MediatorConfig, BusySymbol, MediatorConfigResponse } from '../models/mediatorServer';
index 456ed6d..fb12f26 100644 (file)
@@ -15,7 +15,7 @@
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
+import React from 'react';
 import { Theme, Tooltip } from '@mui/material';
 
 import { WithStyles } from '@mui/styles';
@@ -29,7 +29,7 @@ import DeleteIcon from '@mui/icons-material/Delete';
 import Refresh from '@mui/icons-material/Refresh';
 
 import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
-import connect, { IDispatcher, Connect } from '../../../../framework/src/flux/connect';
+import { connect, IDispatcher, Connect } from '../../../../framework/src/flux/connect';
 import MaterialTable, { MaterialTableCtorType, ColumnType } from '../../../../framework/src/components/material-table';
 
 import { createAvaliableMediatorServersProperties, createAvaliableMediatorServersActions } from '../handlers/avaliableMediatorServersHandler';
index b0c9b42..c950056 100644 (file)
@@ -4,7 +4,7 @@
     "outDir": "./dist",
     "sourceMap": true,
     "forceConsistentCasingInFileNames": true,
-    "allowSyntheticDefaultImports": false,
+    "allowSyntheticDefaultImports": true,
     "allowUnreachableCode": false,
     "allowUnusedLabels": false,
     "noFallthroughCasesInSwitch": true,
index f626632..813c28f 100644 (file)
@@ -57,6 +57,16 @@ module.exports = (env) => {
         use: [{
           loader: "babel-loader"
         }]
+      },{
+        //don't minify images
+        test: /\.(png|gif|jpg|svg)$/,
+        use: [{
+          loader: 'url-loader',
+          options: {
+            limit: 10,
+            name: './images/[name].[ext]'
+          }
+        }]
       }]
     },
 
index 57b0883..a58acd3 100644 (file)
@@ -19,6 +19,7 @@
   ~ ============LICENSE_END=======================================================
   ~
   -->
+
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
                         <!-- optional: default phase is "generate-resources" -->
                         <phase>initialize</phase>
                         <configuration>
-                            <nodeVersion>v12.13.0</nodeVersion>
+                            <nodeVersion>v12.22.0</nodeVersion>
                             <yarnVersion>v1.22.10</yarnVersion>
                         </configuration>
                     </execution>
diff --git a/sdnr/wt/odlux/apps/minimumApp/src/assets/icons/minimumAppIcon.svg b/sdnr/wt/odlux/apps/minimumApp/src/assets/icons/minimumAppIcon.svg
new file mode 100644 (file)
index 0000000..298eaa1
--- /dev/null
@@ -0,0 +1,27 @@
+<!-- highstreet technologies GmbH colour scheme 
+       Grey    #565656
+       LBlue #36A9E1
+       DBlue #246DA2
+       Green #003F2C / #006C4B
+       Yellw #C8D400
+       Red     #D81036
+-->
+
+<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="310 250 400 400">
+
+<g transform="translate(0,1024) scale(0.1,-0.1)">
+
+<path fill="#565656" d="M4926 7634 c-126 -17 -209 -38 -318 -79 -79 -31 -195 -89 -208 -104
+-10 -12 -69 -51 -77 -51 -4 0 -42 -28 -83 -63 -227 -190 -375 -475 -375 -722
+0 -81 3 -95 30 -143 111 -201 365 -252 514 -103 46 46 88 124 121 226 28 87
+109 255 153 315 67 95 172 168 275 192 86 20 268 21 346 2 113 -28 152 -50
+240 -137 64 -63 88 -95 104 -137 49 -125 52 -225 12 -332 -38 -102 -132 -209
+-360 -409 -153 -134 -329 -309 -375 -374 -97 -136 -148 -274 -166 -448 -19
+-192 12 -305 104 -379 64 -50 141 -72 228 -65 82 7 125 24 177 71 49 45 73
+100 105 241 59 258 63 263 528 687 218 198 295 284 374 419 134 230 138 543 9
+803 -101 202 -252 349 -474 461 -246 124 -573 172 -884 129z"/>
+
+<path fill="#36A9E1" d="M 5098 4587 C 4582 4587 4582 3845 5098 3845 C 5614 3847 5614 4585 5098 4587 Z"/>
+
+</g>
+</svg>
index 463ad21..4a4dc61 100644 (file)
@@ -19,6 +19,7 @@
 
 import { combineActionHandler } from '../../../../framework/src/flux/middleware';
 
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
 import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
 
 export interface IMinimumAppStoreState {
@@ -26,7 +27,7 @@ export interface IMinimumAppStoreState {
 
 declare module '../../../../framework/src/store/applicationStore' {
   interface IApplicationStoreState {
-    minimum: IMinimumAppStoreState
+    minimum: IMinimumAppStoreState;
   }
 }
 
index 3095ba7..5c8500a 100644 (file)
  */
 // app configuration and main entry point for the app
 
-import * as React from "react";
-import { withRouter, RouteComponentProps, Route, Switch, Redirect } from 'react-router-dom';
-
-import { faLock } from '@fortawesome/free-solid-svg-icons';  // select app icon
+import React, { FC } from 'react';
+import { withRouter, RouteComponentProps } from 'react-router-dom';
 
 import applicationManager from '../../../framework/src/services/applicationManager';
-import connect, { Connect } from '../../../framework/src/flux/connect';
+import { connect, Connect } from '../../../framework/src/flux/connect';
 
 import { minimumAppRootHandler } from './handlers/minimumAppRootHandler';
 
+// const appIcon = require('./assets/icons/minimunAppIcon.svg');  // select app icon
+
 type AppProps = RouteComponentProps & Connect;
 
-const App = (props: AppProps) => (
+const App: FC<AppProps> = (_props) => (
   <div>Start your app here!!</div>
 );
 
@@ -37,11 +37,11 @@ const FinalApp = withRouter(connect()(App));
 
 export function register() {
   applicationManager.registerApplication({
-    name: "minimum",
-    icon: faLock,
+    name: 'minimum',
+    // icon: appIcon,
     rootComponent: FinalApp,
     rootActionHandler: minimumAppRootHandler,
-    menuEntry: "Minimum"
+    menuEntry: 'Minimum',
   });
 }
 
index a66b5d8..ca65092 100644 (file)
@@ -4,7 +4,7 @@
     "outDir": "./dist",
     "sourceMap": true,
     "forceConsistentCasingInFileNames": true,
-    "allowSyntheticDefaultImports": false,
+    "allowSyntheticDefaultImports": true,
     "allowUnreachableCode": false,
     "allowUnusedLabels": false,
     "noFallthroughCasesInSwitch": true,
index 9c594ef..78e38c0 100644 (file)
@@ -19,6 +19,7 @@
   ~ ============LICENSE_END=======================================================
   ~
   -->
+
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
                         <!-- optional: default phase is "generate-resources" -->
                         <phase>initialize</phase>
                         <configuration>
-                            <nodeVersion>v12.13.0</nodeVersion>
+                            <nodeVersion>v12.22.0</nodeVersion>
                             <yarnVersion>v1.22.10</yarnVersion>
                         </configuration>
                     </execution>
index 2b8e6e2..fbbf2c5 100644 (file)
@@ -17,6 +17,7 @@
  */
 import { Action } from '../../../../framework/src/flux/action';
 import { Dispatch } from '../../../../framework/src/flux/store';
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
 import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
 
 import { DeviceListType } from '../models/deviceListType';
@@ -56,7 +57,7 @@ export const loadAllDeviceListAsync = async (dispatch: Dispatch) => {
   deviceListFromPerf24History.forEach(deviceList24h => {
     if (deviceListFromPerfHistory.findIndex(deviceList15min => deviceList15min.nodeId === deviceList24h.nodeId) < 0) {
       deviceListFromPerfHistory.push(deviceList24h);
-    };
+    }
   });
   return deviceListFromPerfHistory && dispatch(new AllDeviceListLoadedAction(deviceListFromPerfHistory));
 };
@@ -75,4 +76,4 @@ export class UpdateMountId extends BaseAction {
  */
 export const updateMountIdActionCreator = (nodeId: string) => async (dispatch: Dispatch) => {
   return dispatch(new UpdateMountId(nodeId));
-}
+};
index a678ed7..1c333ab 100644 (file)
@@ -50,18 +50,23 @@ export class SetInitialLoadedAction extends BaseAction {
   }
 }
 
-export class NoLtpsFoundAction extends BaseAction {
-  constructor() {
-    super();
-  }
-}
+export class NoLtpsFoundAction extends BaseAction { }
 
-export class ResetLtpsAction extends BaseAction {
-  constructor() {
-    super();
-  }
-}
+export class ResetLtpsAction extends BaseAction { }
 
+const getDistinctLtps = (distinctLtps: LtpIds[], selectedLtp: string, selectFirstLtp?: Function, resetLtp?: Function) => {
+  let ltpNotSelected: boolean = true;
+  // eslint-disable-next-line @typescript-eslint/no-unused-expressions
+  selectFirstLtp && selectFirstLtp(distinctLtps[0].key);
+  distinctLtps.forEach((value: LtpIds) => {
+    if (value.key === selectedLtp) {
+      ltpNotSelected = false;
+    }
+  });
+  // eslint-disable-next-line @typescript-eslint/no-unused-expressions
+  resetLtp && resetLtp(ltpNotSelected);
+  return distinctLtps;
+};
 
 /** 
  * Represents an asynchronous thunk action to load available distinctLtps by networkElement from the database and set the returned first Ltp as default. 
@@ -87,14 +92,3 @@ export const loadDistinctLtpsbyNetworkElementAsync = (networkElement: string, se
   });
 };
 
-const getDistinctLtps = (distinctLtps: LtpIds[], selectedLtp: string, selectFirstLtp?: Function, resetLtp?: Function) => {
-  let ltpNotSelected: boolean = true;
-  selectFirstLtp && selectFirstLtp(distinctLtps[0].key);
-  distinctLtps.forEach((value: LtpIds) => {
-    if (value.key === selectedLtp) {
-      ltpNotSelected = false;
-    }
-  });
-  resetLtp && resetLtp(ltpNotSelected);
-  return distinctLtps;
-}
\ No newline at end of file
index 7d6ecfe..6bd54ce 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
-import { Action } from "../../../../framework/src/flux/action";
+import { Action } from '../../../../framework/src/flux/action';
 
 export class ReloadAction extends Action {
-    constructor(public show: boolean) {
-        super();
-    }
+  constructor(public show: boolean) {
+    super();
+  }
 }
\ No newline at end of file
index a069af1..13214b2 100644 (file)
@@ -16,6 +16,7 @@
  * ============LICENSE_END==========================================================================
  */
 import { Action } from '../../../../framework/src/flux/action';
+
 import { PmDataInterval } from '../models/performanceDataType';
 
 export class TimeChangeAction extends Action {
index 0efaaae..7921ea5 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
-import { Action } from "../../../../framework/src/flux/action";
-import { currentViewType } from "../models/toggleDataType";
+import { Action } from '../../../../framework/src/flux/action';
+
+import { currentViewType } from '../models/toggleDataType';
 
 
 export class SetSubViewAction extends Action {
-    constructor(public currentView: currentViewType, public selectedTab: "chart" | "table") {
-        super();
-    }
+  constructor(public currentView: currentViewType, public selectedTab: 'chart' | 'table') {
+    super();
+  }
 }
 
-export class ResetAllSubViewsAction extends Action {
-    constructor() {
-        super();
-    }
-}
+export class ResetAllSubViewsAction extends Action { }
 
 export class SetFilterVisibility extends Action {
-    constructor(public currentView: currentViewType, public isVisible: boolean) {
-        super();
-    }
+  constructor(public currentView: currentViewType, public isVisible: boolean) {
+    super();
+  }
 }
\ No newline at end of file
diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/assets/icons/performanceHistoryAppIcon.svg b/sdnr/wt/odlux/apps/performanceHistoryApp/src/assets/icons/performanceHistoryAppIcon.svg
new file mode 100644 (file)
index 0000000..982f1ee
--- /dev/null
@@ -0,0 +1,50 @@
+<!-- highstreet technologies GmbH colour scheme \r
+       Grey    #565656\r
+       LBlue #36A9E1\r
+       DBlue #246DA2\r
+       Green #003F2C / #006C4B\r
+       Yellw #C8D400\r
+       Red     #D81036\r
+-->\r
+\r
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 500 500">\r
+<g>\r
+       <g>\r
+               <path fill="#006C4B" d="M 0 447.672 C 0 460.238 4.471 470.99 13.417 479.936 C 22.368 488.882 33.119 493.355 45.681 493.355 H 429.405 C 441.965 493.355 452.717 488.882 461.663 479.936 C 470.608 470.991 475.085 460.239 475.085 447.672 L 0 447.672 Z"/>\r
+               <rect fill="#565656" x="73.092" y="310.635" width="73.089" height="109.632"/>\r
+               <rect fill="#565656" x="182.728" y="164.452" width="73.085" height="255.814"/>\r
+               <rect fill="#565656" x="292.362" y="237.541" width="73.083" height="182.726"/>\r
+               <rect fill="#565656" x="401.994" y="127.907" width="73.091" height="292.36"/>\r
+       </g>\r
+</g>\r
+<g>\r
+</g>\r
+<g>\r
+</g>\r
+<g>\r
+</g>\r
+<g>\r
+</g>\r
+<g>\r
+</g>\r
+<g>\r
+</g>\r
+<g>\r
+</g>\r
+<g>\r
+</g>\r
+<g>\r
+</g>\r
+<g>\r
+</g>\r
+<g>\r
+</g>\r
+<g>\r
+</g>\r
+<g>\r
+</g>\r
+<g>\r
+</g>\r
+<g>\r
+</g>\r
+</svg>\r
index 17b1374..5dac0bc 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
+import React from 'react';
+import { RouteComponentProps, withRouter } from 'react-router-dom';
 
-import { withRouter, RouteComponentProps } from 'react-router-dom';
-
-import { MaterialTable, ColumnType, ColumnModel, MaterialTableCtorType } from '../../../../framework/src/components/material-table';
+import { ColumnModel, ColumnType, MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table';
+import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect';
 import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
-import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect';
 
-import { AdaptiveModulationDataType, AdaptiveModulationDatabaseDataType } from '../models/adaptiveModulationDataType';
+import { SetFilterVisibility, SetSubViewAction } from '../actions/toggleActions';
+import { createAdaptiveModulationActions, createAdaptiveModulationProperties } from '../handlers/adaptiveModulationHandler';
+import { AdaptiveModulationDatabaseDataType, AdaptiveModulationDataType } from '../models/adaptiveModulationDataType';
 import { IDataSet, IDataSetsObject } from '../models/chartTypes';
-import { createAdaptiveModulationProperties, createAdaptiveModulationActions } from '../handlers/adaptiveModulationHandler';
 import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils';
 import { addColumnLabels } from '../utils/tableUtils';
 import ToggleContainer from './toggleContainer';
-import { SetSubViewAction, SetFilterVisibility } from '../actions/toggleActions';
 
 const mapProps = (state: IApplicationStoreState) => ({
   adaptiveModulationProperties: createAdaptiveModulationProperties(state),
   currentView: state.performanceHistory.subViews.adaptiveModulation.subView,
   isFilterVisible: state.performanceHistory.subViews.adaptiveModulation.isFilterVisible,
-  existingFilter: state.performanceHistory.adaptiveModulation.filter
+  existingFilter: state.performanceHistory.adaptiveModulation.filter,
 });
 
 const mapDisp = (dispatcher: IDispatcher) => ({
   adaptiveModulationActions: createAdaptiveModulationActions(dispatcher.dispatch),
-  setSubView: (value: "chart" | "table") => dispatcher.dispatch(new SetSubViewAction("adaptiveModulation", value)),
-  toggleFilterButton: (value: boolean) => { dispatcher.dispatch(new SetFilterVisibility("adaptiveModulation", value)) },
+  setSubView: (value: 'chart' | 'table') => dispatcher.dispatch(new SetSubViewAction('adaptiveModulation', value)),
+  toggleFilterButton: (value: boolean) => { dispatcher.dispatch(new SetFilterVisibility('adaptiveModulation', value)); },
 });
 
 type AdaptiveModulationComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp> & {
-  selectedTimePeriod: string
+  selectedTimePeriod: string;
 };
 
 const AdaptiveModulationTable = MaterialTable as MaterialTableCtorType<AdaptiveModulationDataType>;
@@ -53,21 +52,20 @@ const AdaptiveModulationTable = MaterialTable as MaterialTableCtorType<AdaptiveM
 /**
  * The Component which gets the adaptiveModulation data from the database based on the selected time period.
  */
-class AdaptiveModulationComponent extends React.Component<AdaptiveModulationComponentProps>{
-
+class AdaptiveModulationComponent extends React.Component<AdaptiveModulationComponentProps> {
   onToggleFilterButton = () => {
     this.props.toggleFilterButton(!this.props.isFilterVisible);
-  }
+  };
 
-  onChange = (value: "chart" | "table") => {
+  onChange = (value: 'chart' | 'table') => {
     this.props.setSubView(value);
-  }
+  };
 
   onFilterChanged = (property: string, filterTerm: string) => {
     this.props.adaptiveModulationActions.onFilterChanged(property, filterTerm);
     if (!this.props.adaptiveModulationProperties.showFilter)
       this.props.adaptiveModulationActions.onToggleFilter(false);
-  }
+  };
 
   render(): JSX.Element {
     const properties = this.props.adaptiveModulationProperties;
@@ -75,11 +73,11 @@ class AdaptiveModulationComponent extends React.Component<AdaptiveModulationComp
 
     const chartPagedData = this.getChartDataValues(properties.rows);
     const adaptiveModulationColumns: ColumnModel<AdaptiveModulationDataType>[] = [
-      { property: "radioSignalId", title: "Radio signal", type: ColumnType.text },
-      { property: "scannerId", title: "Scanner ID", type: ColumnType.text },
-      { property: "timeStamp", title: "End Time", type: ColumnType.text },
+      { property: 'radioSignalId', title: 'Radio signal', type: ColumnType.text },
+      { property: 'scannerId', title: 'Scanner ID', type: ColumnType.text },
+      { property: 'timeStamp', title: 'End Time', type: ColumnType.text },
       {
-        property: "suspectIntervalFlag", title: "Suspect Interval", type: ColumnType.boolean
+        property: 'suspectIntervalFlag', title: 'Suspect Interval', type: ColumnType.boolean,
       }];
 
     chartPagedData.datasets.forEach(ds => {
@@ -88,13 +86,14 @@ class AdaptiveModulationComponent extends React.Component<AdaptiveModulationComp
 
     return (
       <>
-        <ToggleContainer onToggleFilterButton={this.onToggleFilterButton} showFilter={this.props.isFilterVisible} existingFilter={this.props.adaptiveModulationProperties.filter} onFilterChanged={this.onFilterChanged} selectedValue={this.props.currentView} onChange={this.onChange}>
+        <ToggleContainer onToggleFilterButton={this.onToggleFilterButton} showFilter={this.props.isFilterVisible}
+          existingFilter={this.props.adaptiveModulationProperties.filter} onFilterChanged={this.onFilterChanged} selectedValue={this.props.currentView} onChange={this.onChange}>
           {lineChart(chartPagedData)}
-          <AdaptiveModulationTable stickyHeader idProperty={"_id"} tableId="adaptive-modulation-table" columns={adaptiveModulationColumns} {...properties} {...actions} />
+          <AdaptiveModulationTable stickyHeader idProperty={'_id'} tableId="adaptive-modulation-table" columns={adaptiveModulationColumns} {...properties} {...actions} />
         </ToggleContainer>
       </>
     );
-  };
+  }
 
   /**
    * This function gets the performance values for Adaptive modulation according on the chartjs dataset structure 
@@ -102,345 +101,345 @@ class AdaptiveModulationComponent extends React.Component<AdaptiveModulationComp
    */
 
   private getChartDataValues = (rows: AdaptiveModulationDataType[]): IDataSetsObject => {
-    const _rows = [...rows];
-    sortDataByTimeStamp(_rows);
+    const data_rows = [...rows];
+    sortDataByTimeStamp(data_rows);
 
     const datasets: IDataSet[] = [{
-      name: "time2StatesS",
-      label: "QAM2S",
+      name: 'time2StatesS',
+      label: 'QAM2S',
       borderColor: '#62a309fc',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM2S",
+      columnLabel: 'QAM2S',
     }, {
-      name: "time2States",
-      label: "QAM2",
+      name: 'time2States',
+      label: 'QAM2',
       borderColor: '#62a309fc',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM2",
+      columnLabel: 'QAM2',
     }, {
-      name: "time2StatesL",
-      label: "QAM2L",
+      name: 'time2StatesL',
+      label: 'QAM2L',
       borderColor: '#62a309fc',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM2L",
+      columnLabel: 'QAM2L',
     }, {
-      name: "time4StatesS",
-      label: "QAM4S",
+      name: 'time4StatesS',
+      label: 'QAM4S',
       borderColor: '#b308edde',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM4S",
+      columnLabel: 'QAM4S',
     }, {
-      name: "time4States",
-      label: "QAM4",
+      name: 'time4States',
+      label: 'QAM4',
       borderColor: '#b308edde',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM4",
+      columnLabel: 'QAM4',
     }, {
-      name: "time4StatesL",
-      label: "QAM4L",
+      name: 'time4StatesL',
+      label: 'QAM4L',
       borderColor: '#b308edde',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM4L",
+      columnLabel: 'QAM4L',
     }, {
-      name: "time16StatesS",
-      label: "QAM16S",
+      name: 'time16StatesS',
+      label: 'QAM16S',
       borderColor: '#9b15e2',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM16S",
+      columnLabel: 'QAM16S',
     }, {
-      name: "time16States",
-      label: "QAM16",
+      name: 'time16States',
+      label: 'QAM16',
       borderColor: '#9b15e2',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM16",
+      columnLabel: 'QAM16',
     }, {
-      name: "time16StatesL",
-      label: "QAM16L",
+      name: 'time16StatesL',
+      label: 'QAM16L',
       borderColor: '#9b15e2',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM16L",
+      columnLabel: 'QAM16L',
     }, {
-      name: "time32StatesS",
-      label: "QAM32S",
+      name: 'time32StatesS',
+      label: 'QAM32S',
       borderColor: '#2704f5f0',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM32S",
+      columnLabel: 'QAM32S',
     }, {
-      name: "time32States",
-      label: "QAM32",
+      name: 'time32States',
+      label: 'QAM32',
       borderColor: '#2704f5f0',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM32",
+      columnLabel: 'QAM32',
     }, {
-      name: "time32StatesL",
-      label: "QAM32L",
+      name: 'time32StatesL',
+      label: 'QAM32L',
       borderColor: '#2704f5f0',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM32L",
+      columnLabel: 'QAM32L',
     }, {
-      name: "time64StatesS",
-      label: "QAM64S",
+      name: 'time64StatesS',
+      label: 'QAM64S',
       borderColor: '#347692',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM64S",
+      columnLabel: 'QAM64S',
     }, {
-      name: "time64States",
-      label: "QAM64",
+      name: 'time64States',
+      label: 'QAM64',
       borderColor: '#347692',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM64",
+      columnLabel: 'QAM64',
     }, {
-      name: "time64StatesL",
-      label: "QAM64L",
+      name: 'time64StatesL',
+      label: 'QAM64L',
       borderColor: '#347692',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM64L",
+      columnLabel: 'QAM64L',
     }, {
-      name: "time128StatesS",
-      label: "QAM128S",
+      name: 'time128StatesS',
+      label: 'QAM128S',
       borderColor: '#885e22',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM128S",
+      columnLabel: 'QAM128S',
     }, {
-      name: "time128States",
-      label: "QAM128",
+      name: 'time128States',
+      label: 'QAM128',
       borderColor: '#885e22',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM128",
+      columnLabel: 'QAM128',
     }, {
-      name: "time128StatesL",
-      label: "QAM128L",
+      name: 'time128StatesL',
+      label: 'QAM128L',
       borderColor: '#885e22',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM128L",
+      columnLabel: 'QAM128L',
     }, {
-      name: "time256StatesS",
-      label: "QAM256S",
+      name: 'time256StatesS',
+      label: 'QAM256S',
       borderColor: '#de07807a',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM256S",
+      columnLabel: 'QAM256S',
     }, {
-      name: "time256States",
-      label: "QAM256",
+      name: 'time256States',
+      label: 'QAM256',
       borderColor: '#de07807a',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM256",
+      columnLabel: 'QAM256',
     }, {
-      name: "time256StatesL",
-      label: "QAM256L",
+      name: 'time256StatesL',
+      label: 'QAM256L',
       borderColor: '#de07807a',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM256L",
+      columnLabel: 'QAM256L',
     }, {
-      name: "time512StatesS",
-      label: "QAM512S",
+      name: 'time512StatesS',
+      label: 'QAM512S',
       borderColor: '#8fdaacde',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM512S",
+      columnLabel: 'QAM512S',
     }, {
-      name: "time512States",
-      label: "QAM512",
+      name: 'time512States',
+      label: 'QAM512',
       borderColor: '#8fdaacde',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM512",
+      columnLabel: 'QAM512',
     }, {
 
-      name: "time512StatesL",
-      label: "QAM512L",
+      name: 'time512StatesL',
+      label: 'QAM512L',
       borderColor: '#8fdaacde',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM512L",
+      columnLabel: 'QAM512L',
     }, {
 
-      name: "time1024StatesS",
-      label: "QAM1024S",
+      name: 'time1024StatesS',
+      label: 'QAM1024S',
       borderColor: '#435b22',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM1024S",
+      columnLabel: 'QAM1024S',
     }, {
 
-      name: "time1024States",
-      label: "QAM1024",
+      name: 'time1024States',
+      label: 'QAM1024',
       borderColor: '#435b22',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM1024",
+      columnLabel: 'QAM1024',
     }, {
 
-      name: "time1024StatesL",
-      label: "QAM1024L",
+      name: 'time1024StatesL',
+      label: 'QAM1024L',
       borderColor: '#435b22',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM1024L",
+      columnLabel: 'QAM1024L',
     }, {
-      name: "time2048StatesS",
-      label: "QAM2048S",
+      name: 'time2048StatesS',
+      label: 'QAM2048S',
       borderColor: '#e87a5b',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM2048S",
+      columnLabel: 'QAM2048S',
     }, {
-      name: "time2048States",
-      label: "QAM2048",
+      name: 'time2048States',
+      label: 'QAM2048',
       borderColor: '#e87a5b',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM2048",
+      columnLabel: 'QAM2048',
     }, {
-      name: "time2048StatesL",
-      label: "QAM2048L",
+      name: 'time2048StatesL',
+      label: 'QAM2048L',
       borderColor: '#e87a5b',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM2048L",
+      columnLabel: 'QAM2048L',
     }, {
-      name: "time4096StatesS",
-      label: "QAM4096S",
+      name: 'time4096StatesS',
+      label: 'QAM4096S',
       borderColor: '#5be878',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM4096S",
+      columnLabel: 'QAM4096S',
     }, {
-      name: "time4096States",
-      label: "QAM4096",
+      name: 'time4096States',
+      label: 'QAM4096',
       borderColor: '#5be878',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM4096",
+      columnLabel: 'QAM4096',
     }, {
-      name: "time4096StatesL",
-      label: "QAM4096L",
+      name: 'time4096StatesL',
+      label: 'QAM4096L',
       borderColor: '#5be878',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM4096L",
+      columnLabel: 'QAM4096L',
     }, {
-      name: "time8192StatesS",
-      label: "QAM8192s",
+      name: 'time8192StatesS',
+      label: 'QAM8192s',
       borderColor: '#cb5be8',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM8192S",
+      columnLabel: 'QAM8192S',
     }, {
-      name: "time8192States",
-      label: "QAM8192",
+      name: 'time8192States',
+      label: 'QAM8192',
       borderColor: '#cb5be8',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM8192",
+      columnLabel: 'QAM8192',
     }, {
-      name: "time8192StatesL",
-      label: "QAM8192L",
+      name: 'time8192StatesL',
+      label: 'QAM8192L',
       borderColor: '#cb5be8',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "QAM8192L",
-    }
+      columnLabel: 'QAM8192L',
+    },
     ];
 
-    _rows.forEach(row => {
-      row.time2StatesS = row.performanceData.time2StatesS
+    data_rows.forEach(row => {
+      row.time2StatesS = row.performanceData.time2StatesS;
       row.time2States = row.performanceData.time2States;
       row.time2StatesL = row.performanceData.time2StatesL;
-      row.time4StatesS = row.performanceData.time4StatesS
+      row.time4StatesS = row.performanceData.time4StatesS;
       row.time4States = row.performanceData.time4States;
       row.time4StatesL = row.performanceData.time4StatesL;
       row.time16StatesS = row.performanceData.time16StatesS;
@@ -475,16 +474,16 @@ class AdaptiveModulationComponent extends React.Component<AdaptiveModulationComp
       row.time8192StatesL = row.performanceData.time8192StatesL;
       datasets.forEach(ds => {
         ds.data.push({
-          x: row["timeStamp" as keyof AdaptiveModulationDataType] as string,
-          y: row.performanceData[ds.name as keyof AdaptiveModulationDatabaseDataType] as string
+          x: row['timeStamp' as keyof AdaptiveModulationDataType] as string,
+          y: row.performanceData[ds.name as keyof AdaptiveModulationDatabaseDataType] as string,
         });
       });
     });
 
     return {
-      datasets: datasets
+      datasets: datasets,
     };
-  }
+  };
 }
 const AdaptiveModulation = withRouter(connect(mapProps, mapDisp)(AdaptiveModulationComponent));
 export default AdaptiveModulation;
index e66e6c1..021f74a 100644 (file)
 
 
 import * as React from 'react';
-import { TextField, Typography, Select, MenuItem, FormControl, InputLabel } from '@mui/material';
+
+import { TextField, Select, MenuItem, FormControl, InputLabel } from '@mui/material';
 
 import makeStyles from '@mui/styles/makeStyles';
 
 const styles = makeStyles({
-    filterInput: {
-        marginRight: "15px"
-    },
-    filterContainer: {
-        marginLeft: "90px"
-    }
+  filterInput: {
+    marginRight: '15px',
+  },
+  filterContainer: {
+    marginLeft: '90px',
+  },
 });
 
-type filterProps = { isVisible: boolean, onFilterChanged: (property: string, filterTerm: string) => void, filters: any };
+type filterProps = { isVisible: boolean; onFilterChanged: (property: string, filterTerm: string) => void; filters: any };
 
 const ChartFilter: React.FunctionComponent<filterProps> = (props) => {
 
 
-    const classes = styles();
-
-    // make sure suspectIntervalFlag is a string to show the correct value in the select element
+  const classes = styles();
 
-    const suspectIntervalFlag = props.filters.suspectIntervalFlag === undefined ? undefined : props.filters.suspectIntervalFlag.toString();
-    return (
-        <>
-            {
-                props.isVisible &&
-                <div className={classes.filterContainer}>
-                    <TextField variant="standard" inputProps={{'aria-label': 'radio-signal-filter'}} className={classes.filterInput} label="Radio Signal" value={props.filters.radioSignalId || ''} onChange={(event) => props.onFilterChanged("radioSignalId", event.target.value)} InputLabelProps={{
-                        shrink: true,
-                    }} />
-                    <TextField variant="standard" inputProps={{'aria-label': 'scanner-id-filter'}} className={classes.filterInput} label="Scanner ID" value={props.filters.scannerId || ''} onChange={(event) => props.onFilterChanged("scannerId", event.target.value)} InputLabelProps={{
-                        shrink: true,
-                    }} />
-                    <TextField variant="standard" inputProps={{'aria-label': 'end-time-filter'}} className={classes.filterInput} label="End Time" value={props.filters.timeStamp || ''} onChange={(event) => props.onFilterChanged("timeStamp", event.target.value)} InputLabelProps={{
-                        shrink: true,
-                    }} />
-                    <FormControl variant="standard">
-                        <InputLabel id="suspect-interval-label" shrink>Suspect Interval</InputLabel>
+  // make sure suspectIntervalFlag is a string to show the correct value in the select element
 
-                        <Select variant="standard" aria-label="suspect-interval-selection" labelId="suspect-interval-label" value={suspectIntervalFlag || ''} onChange={(event) => props.onFilterChanged("suspectIntervalFlag", event.target.value as string)}>
-                            <MenuItem value={undefined} aria-label="none">None</MenuItem>
-                            <MenuItem value={"true"} aria-label="true">true</MenuItem>
-                            <MenuItem value={"false"} aria-label="false">false</MenuItem>
-                        </Select>
-                    </FormControl>
-                </ div>
-            }
-        </>
-    )
+  const suspectIntervalFlag = props.filters.suspectIntervalFlag === undefined ? undefined : props.filters.suspectIntervalFlag.toString();
+  return (
+    <>
+      {
+        props.isVisible &&
+        <div className={classes.filterContainer}>
+          <TextField variant="standard" inputProps={{ 'aria-label': 'radio-signal-filter' }} className={classes.filterInput}
+            label="Radio Signal" value={props.filters.radioSignalId || ''} onChange={(event) => props.onFilterChanged('radioSignalId', event.target.value)} InputLabelProps={{
+              shrink: true,
+            }} />
+          <TextField variant="standard" inputProps={{ 'aria-label': 'scanner-id-filter' }} className={classes.filterInput} label="Scanner ID" value={props.filters.scannerId || ''} onChange={(event) => props.onFilterChanged('scannerId', event.target.value)} InputLabelProps={{
+            shrink: true,
+          }} />
+          <TextField variant="standard" inputProps={{ 'aria-label': 'end-time-filter' }} className={classes.filterInput} label="End Time" value={props.filters.timeStamp || ''} onChange={(event) => props.onFilterChanged('timeStamp', event.target.value)} InputLabelProps={{
+            shrink: true,
+          }} />
+          <FormControl variant="standard">
+            <InputLabel id="suspect-interval-label" shrink>Suspect Interval</InputLabel>
 
-}
+            <Select variant="standard" aria-label="suspect-interval-selection" labelId="suspect-interval-label" value={suspectIntervalFlag || ''} onChange={(event) => props.onFilterChanged('suspectIntervalFlag', event.target.value as string)}>
+              <MenuItem value={undefined} aria-label="none">None</MenuItem>
+              <MenuItem value={'true'} aria-label="true">true</MenuItem>
+              <MenuItem value={'false'} aria-label="false">false</MenuItem>
+            </Select>
+          </FormControl>
+        </ div>
+      }
+    </>
+  );
+};
 
 export default ChartFilter;
\ No newline at end of file
index 14cc02d..5f925a9 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
+import React from 'react';
+import { RouteComponentProps, withRouter } from 'react-router-dom';
 
-import { withRouter, RouteComponentProps } from 'react-router-dom';
-
-import { MaterialTable, ColumnType, MaterialTableCtorType, ColumnModel } from '../../../../framework/src/components/material-table';
+import { ColumnModel, ColumnType, MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table';
+import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect';
 import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
-import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect';
 
-import { CrossPolarDiscriminationDataType, CrossPolarDiscriminationDatabaseDataType } from '../models/crossPolarDiscriminationDataType';
+import { SetFilterVisibility, SetSubViewAction } from '../actions/toggleActions';
+import { createCrossPolarDiscriminationActions, createCrossPolarDiscriminationProperties } from '../handlers/crossPolarDiscriminationHandler';
 import { IDataSet, IDataSetsObject } from '../models/chartTypes';
-import { createCrossPolarDiscriminationProperties, createCrossPolarDiscriminationActions } from '../handlers/crossPolarDiscriminationHandler';
+import { CrossPolarDiscriminationDatabaseDataType, CrossPolarDiscriminationDataType } from '../models/crossPolarDiscriminationDataType';
 import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils';
 import { addColumnLabels } from '../utils/tableUtils';
-import { SetSubViewAction, SetFilterVisibility } from '../actions/toggleActions';
 import ToggleContainer from './toggleContainer';
 
 
@@ -36,18 +35,18 @@ const mapProps = (state: IApplicationStoreState) => ({
   crossPolarDiscriminationProperties: createCrossPolarDiscriminationProperties(state),
   currentView: state.performanceHistory.subViews.CPD.subView,
   isFilterVisible: state.performanceHistory.subViews.CPD.isFilterVisible,
-  existingFilter: state.performanceHistory.crossPolarDiscrimination.filter
+  existingFilter: state.performanceHistory.crossPolarDiscrimination.filter,
 
 });
 
 const mapDisp = (dispatcher: IDispatcher) => ({
   crossPolarDiscriminationActions: createCrossPolarDiscriminationActions(dispatcher.dispatch),
-  setSubView: (value: "chart" | "table") => dispatcher.dispatch(new SetSubViewAction("CPD", value)),
-  toggleFilterButton: (value: boolean) => { dispatcher.dispatch(new SetFilterVisibility("CPD", value)) },
+  setSubView: (value: 'chart' | 'table') => dispatcher.dispatch(new SetSubViewAction('CPD', value)),
+  toggleFilterButton: (value: boolean) => { dispatcher.dispatch(new SetFilterVisibility('CPD', value));},
 });
 
 type CrossPolarDiscriminationComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp> & {
-  selectedTimePeriod: string
+  selectedTimePeriod: string;
 };
 
 const CrossPolarDiscriminationTable = MaterialTable as MaterialTableCtorType<CrossPolarDiscriminationDataType>;
@@ -55,21 +54,20 @@ const CrossPolarDiscriminationTable = MaterialTable as MaterialTableCtorType<Cro
 /**
  * The Component which gets the crossPolarDiscrimination data from the database based on the selected time period.
  */
-class CrossPolarDiscriminationComponent extends React.Component<CrossPolarDiscriminationComponentProps>{
-
+class CrossPolarDiscriminationComponent extends React.Component<CrossPolarDiscriminationComponentProps> {
   onToggleFilterButton = () => {
     this.props.toggleFilterButton(!this.props.isFilterVisible);
-  }
+  };
 
-  onChange = (value: "chart" | "table") => {
+  onChange = (value: 'chart' | 'table') => {
     this.props.setSubView(value);
-  }
+  };
 
   onFilterChanged = (property: string, filterTerm: string) => {
     this.props.crossPolarDiscriminationActions.onFilterChanged(property, filterTerm);
     if (!this.props.crossPolarDiscriminationProperties.showFilter)
       this.props.crossPolarDiscriminationActions.onToggleFilter(false);
-  }
+  };
 
   render(): JSX.Element {
     const properties = this.props.crossPolarDiscriminationProperties;
@@ -78,12 +76,12 @@ class CrossPolarDiscriminationComponent extends React.Component<CrossPolarDiscri
     const chartPagedData = this.getChartDataValues(properties.rows);
 
     const cpdColumns: ColumnModel<CrossPolarDiscriminationDataType>[] = [
-      { property: "radioSignalId", title: "Radio signal", type: ColumnType.text },
-      { property: "scannerId", title: "Scanner ID", type: ColumnType.text },
-      { property: "timeStamp", title: "End Time", type: ColumnType.text },
+      { property: 'radioSignalId', title: 'Radio signal', type: ColumnType.text },
+      { property: 'scannerId', title: 'Scanner ID', type: ColumnType.text },
+      { property: 'timeStamp', title: 'End Time', type: ColumnType.text },
       {
-        property: "suspectIntervalFlag", title: "Suspect Interval", type: ColumnType.boolean
-      }
+        property: 'suspectIntervalFlag', title: 'Suspect Interval', type: ColumnType.boolean,
+      },
     ];
 
     chartPagedData.datasets.forEach(ds => {
@@ -91,66 +89,67 @@ class CrossPolarDiscriminationComponent extends React.Component<CrossPolarDiscri
     });
     return (
       <>
-        <ToggleContainer onToggleFilterButton={this.onToggleFilterButton} showFilter={this.props.isFilterVisible} existingFilter={this.props.crossPolarDiscriminationProperties.filter} onFilterChanged={this.onFilterChanged} selectedValue={this.props.currentView} onChange={this.onChange}>
+        <ToggleContainer onToggleFilterButton={this.onToggleFilterButton} showFilter={this.props.isFilterVisible}
+          existingFilter={this.props.crossPolarDiscriminationProperties.filter} onFilterChanged={this.onFilterChanged} selectedValue={this.props.currentView} onChange={this.onChange}>
           {lineChart(chartPagedData)}
-          <CrossPolarDiscriminationTable stickyHeader idProperty={"_id"} tableId="cross-polar-discrimination-table" columns={cpdColumns} {...properties} {...actions} />
+          <CrossPolarDiscriminationTable stickyHeader idProperty={'_id'} tableId="cross-polar-discrimination-table" columns={cpdColumns} {...properties} {...actions} />
         </ToggleContainer>
       </>
     );
-  };
+  }
 
   /**
    * This function gets the performance values for CPD according on the chartjs dataset structure 
    * which is to be sent to the chart.
    */
   private getChartDataValues = (rows: CrossPolarDiscriminationDataType[]): IDataSetsObject => {
-    const _rows = [...rows];
-    sortDataByTimeStamp(_rows);
+    const data_rows = [...rows];
+    sortDataByTimeStamp(data_rows);
 
     const datasets: IDataSet[] = [{
-      name: "xpdMin",
-      label: "xpd-min",
+      name: 'xpdMin',
+      label: 'xpd-min',
       borderColor: '#0e17f3de',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "CPD (min)[db]"
+      columnLabel: 'CPD (min)[db]',
     }, {
-      name: "xpdAvg",
-      label: "xpd-avg",
+      name: 'xpdAvg',
+      label: 'xpd-avg',
       borderColor: '#08edb6de',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "CPD (avg)[db]"
+      columnLabel: 'CPD (avg)[db]',
     }, {
-      name: "xpdMax",
-      label: "xpd-max",
+      name: 'xpdMax',
+      label: 'xpd-max',
       borderColor: '#b308edde',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "CPD (max)[db]"
+      columnLabel: 'CPD (max)[db]',
     }];
 
-    _rows.forEach(row => {
+    data_rows.forEach(row => {
       row.xpdMin = row.performanceData.xpdMin;
       row.xpdAvg = row.performanceData.xpdAvg;
       row.xpdMax = row.performanceData.xpdMax;
       datasets.forEach(ds => {
         ds.data.push({
-          x: row["timeStamp" as keyof CrossPolarDiscriminationDataType] as string,
-          y: row.performanceData[ds.name as keyof CrossPolarDiscriminationDatabaseDataType] as string
+          x: row['timeStamp' as keyof CrossPolarDiscriminationDataType] as string,
+          y: row.performanceData[ds.name as keyof CrossPolarDiscriminationDatabaseDataType] as string,
         });
       });
     });
     return {
-      datasets: datasets
+      datasets: datasets,
     };
-  }
+  };
 }
 const CrossPolarDiscrimination = withRouter(connect(mapProps, mapDisp)(CrossPolarDiscriminationComponent));
 export default CrossPolarDiscrimination;
index ef6cfc7..bd6333b 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
+import React from 'react';
 
-import * as React from 'react';
-import { MenuItem, Select, FormControl, Typography, SelectChangeEvent } from '@mui/material';
+import { FormControl, MenuItem, Select, SelectChangeEvent, Typography } from '@mui/material';
+import { Theme } from '@mui/material/styles';
 import makeStyles from '@mui/styles/makeStyles';
-import { LtpIds } from 'models/availableLtps';
 import { Loader } from '../../../../framework/src/components/material-ui';
-
-import { Theme } from '@mui/material/styles';
+import { LtpIds } from '../models/availableLtps';
 
 const useStyles = makeStyles((theme: Theme) => ({
-    display: {
-        display: "inline-block"
-    },
-    selectDropdown: {
-        borderRadius: 1,
-        position: "relative",
-        backgroundColor: theme.palette.background.paper,
-        border: "1px solid #ced4da",
-        fontSize: 16,
-        width: "auto",
-        padding: "5px 5px 5px 5px",
-        transition: theme.transitions.create(["border-color", "box-shadow"]),
-    },
-    center: {
-        "flex": "1",
-        "height": "100%",
-        "display": "flex",
-        "alignItems": "center",
-        "justifyContent": "center",
-        flexDirection: "column"
-    }
+  display: {
+    display: 'inline-block',
+  },
+  selectDropdown: {
+    borderRadius: 1,
+    position: 'relative',
+    backgroundColor: theme.palette.background.paper,
+    border: '1px solid #ced4da',
+    fontSize: 16,
+    width: 'auto',
+    padding: '5px 5px 5px 5px',
+    transition: theme.transitions.create(['border-color', 'box-shadow']),
+  },
+  center: {
+    'flex': '1',
+    'height': '100%',
+    'display': 'flex',
+    'alignItems': 'center',
+    'justifyContent': 'center',
+    flexDirection: 'column',
+  },
 }));
 
-type LtpSelectionProps = { selectedNE: string, error?: string, finishedLoading: boolean, selectedLtp: string, 
-    availableLtps: LtpIds[], 
-    onChangeLtp(event: SelectChangeEvent<HTMLSelectElement | string> ): void, 
-    selectedTimePeriod: string, 
-    onChangeTimePeriod(event: SelectChangeEvent<HTMLSelectElement | string> ): void };
+type LtpSelectionProps = {
+  selectedNE: string; error?: string; finishedLoading: boolean; selectedLtp: string;
+  availableLtps: LtpIds[];
+  onChangeLtp(event: SelectChangeEvent<HTMLSelectElement | string>): void;
+  selectedTimePeriod: string;
+  onChangeTimePeriod(event: SelectChangeEvent<HTMLSelectElement | string>): void;
+};
 
 export const LtpSelection = (props: LtpSelectionProps) => {
-    const classes = useStyles();
-    return (
-        <>
-            <h3>Selected Network Element: {props.selectedNE} </h3>
-            <FormControl variant="standard" className={classes.display}>
-                <span>
-                    Select LTP
-                </span>
-                <Select variant="standard" className={classes.selectDropdown} value={props.selectedLtp} onChange={props.onChangeLtp} aria-label="ltp-selection" >
-                    <MenuItem value={"-1"} aria-label="none"><em>--Select--</em></MenuItem>
-                    {props.availableLtps.map(ltp =>
-                        (<MenuItem value={ltp.key} key={ltp.key} aria-label={ltp.key}>{ltp.key}</MenuItem>))}
-                </Select>
-                <span> Time-Period </span>
-                <Select variant="standard" className={classes.selectDropdown} value={props.selectedTimePeriod} onChange={props.onChangeTimePeriod} aria-label="time-period-selection">
-                    <MenuItem value={"15min"} aria-label="15minutes">15min</MenuItem>
-                    <MenuItem value={"24hours"} aria-label="24hours">24hours</MenuItem>
-                </Select>
-            </FormControl>
-            {
-                !props.finishedLoading && !props.error &&
-                <div className={classes.center}>
-                    <Loader />
-                    <h3>Collecting Data ...</h3>
-                </div>
-            }
-            {
-                props.finishedLoading && props.error &&
-                <div className={classes.center}>
-                    <h3>Data couldn't be loaded</h3>
-                    <Typography variant="body1">{props.error}</Typography>
-                </div>
-            }
-            {
-                props.selectedLtp === "-1" && props.finishedLoading && !props.error && (props.availableLtps.length > 0 ?
-                    <div className={classes.center}>
-                        <h3>Please select a LTP</h3>
-                    </div>
-                    :
-                    <div className={classes.center}>
-                        <h3>No performance data found</h3>
-                    </div>)
-            }
-        </>)
-}
+  const classes = useStyles();
+  return (
+    <>
+      <h3>Selected Network Element: {props.selectedNE} </h3>
+      <FormControl variant="standard" className={classes.display}>
+        <span>
+          Select LTP
+        </span>
+        <Select variant="standard" className={classes.selectDropdown} value={props.selectedLtp} onChange={props.onChangeLtp} aria-label="ltp-selection" >
+          <MenuItem value={'-1'} aria-label="none"><em>--Select--</em></MenuItem>
+          {props.availableLtps.map(ltp =>
+            (<MenuItem value={ltp.key} key={ltp.key} aria-label={ltp.key}>{ltp.key}</MenuItem>))}
+        </Select>
+        <span> Time-Period </span>
+        <Select variant="standard" className={classes.selectDropdown} value={props.selectedTimePeriod} onChange={props.onChangeTimePeriod} aria-label="time-period-selection">
+          <MenuItem value={'15min'} aria-label="15minutes">15min</MenuItem>
+          <MenuItem value={'24hours'} aria-label="24hours">24hours</MenuItem>
+        </Select>
+      </FormControl>
+      {
+        !props.finishedLoading && !props.error &&
+        <div className={classes.center}>
+          <Loader />
+          <h3>Collecting Data ...</h3>
+        </div>
+      }
+      {
+        props.finishedLoading && props.error &&
+        <div className={classes.center}>
+          <h3>Data couldn't be loaded</h3>
+          <Typography variant="body1">{props.error}</Typography>
+        </div>
+      }
+      {
+        props.selectedLtp === '-1' && props.finishedLoading && !props.error && (props.availableLtps.length > 0 ?
+          <div className={classes.center}>
+            <h3>Please select a LTP</h3>
+          </div>
+          :
+          <div className={classes.center}>
+            <h3>No performance data found</h3>
+          </div>)
+      }
+    </>);
+};
 
 export default LtpSelection;
\ No newline at end of file
index 6a06ea3..fb608aa 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
+import React from 'react';
+import { RouteComponentProps, withRouter } from 'react-router-dom';
 
-import { withRouter, RouteComponentProps } from 'react-router-dom';
-
-import { MaterialTable, ColumnType, ColumnModel, MaterialTableCtorType } from '../../../../framework/src/components/material-table';
+import { ColumnModel, ColumnType, MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table';
+import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect';
 import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
-import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect';
-import { PerformanceDataType, PerformanceDatabaseDataType } from '../models/performanceDataType';
+
+import { SetFilterVisibility, SetSubViewAction } from '../actions/toggleActions';
+import { createPerformanceDataActions, createPerformanceDataProperties } from '../handlers/performanceDataHandler';
 import { IDataSet, IDataSetsObject } from '../models/chartTypes';
-import { createPerformanceDataProperties, createPerformanceDataActions } from '../handlers/performanceDataHandler';
+import { PerformanceDatabaseDataType, PerformanceDataType } from '../models/performanceDataType';
 import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils';
 import { addColumnLabels } from '../utils/tableUtils';
 import ToggleContainer from './toggleContainer';
-import { SetSubViewAction, SetFilterVisibility } from '../actions/toggleActions';
 
 const mapProps = (state: IApplicationStoreState) => ({
   performanceDataProperties: createPerformanceDataProperties(state),
   currentView: state.performanceHistory.subViews.performanceData.subView,
   isFilterVisible: state.performanceHistory.subViews.performanceData.isFilterVisible,
-  existingFilter: state.performanceHistory.performanceData.filter
+  existingFilter: state.performanceHistory.performanceData.filter,
 });
 
 const mapDisp = (dispatcher: IDispatcher) => ({
   performanceDataActions: createPerformanceDataActions(dispatcher.dispatch),
-  setSubView: (value: "chart" | "table") => dispatcher.dispatch(new SetSubViewAction("performanceData", value)),
-  toggleFilterButton: (value: boolean) => { dispatcher.dispatch(new SetFilterVisibility("performanceData", value)) }
+  setSubView: (value: 'chart' | 'table') => dispatcher.dispatch(new SetSubViewAction('performanceData', value)),
+  toggleFilterButton: (value: boolean) => { dispatcher.dispatch(new SetFilterVisibility('performanceData', value)); },
 });
 
 type PerformanceDataComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp> & {
-  selectedTimePeriod: string
+  selectedTimePeriod: string;
 };
 
 const PerformanceDataTable = MaterialTable as MaterialTableCtorType<PerformanceDataType>;
@@ -52,17 +52,16 @@ const PerformanceDataTable = MaterialTable as MaterialTableCtorType<PerformanceD
 /**
  * The Component which gets the performance data from the database based on the selected time period.
  */
-class PerformanceDataComponent extends React.Component<PerformanceDataComponentProps>{
-
+class PerformanceDataComponent extends React.Component<PerformanceDataComponentProps> {
   onToggleFilterButton = () => {
     this.props.toggleFilterButton(!this.props.isFilterVisible);
-  }
+  };
 
   onFilterChanged = (property: string, filterTerm: string) => {
     this.props.performanceDataActions.onFilterChanged(property, filterTerm);
     if (!this.props.performanceDataProperties.showFilter)
       this.props.performanceDataActions.onToggleFilter(false);
-  }
+  };
 
   render(): JSX.Element {
     const properties = this.props.performanceDataProperties;
@@ -70,12 +69,12 @@ class PerformanceDataComponent extends React.Component<PerformanceDataComponentP
 
     const chartPagedData = this.getChartDataValues(properties.rows);
     const performanceColumns: ColumnModel<PerformanceDataType>[] = [
-      { property: "radioSignalId", title: "Radio signal", type: ColumnType.text },
-      { property: "scannerId", title: "Scanner ID", type: ColumnType.text },
-      { property: "timeStamp", title: "End Time", type: ColumnType.text },
+      { property: 'radioSignalId', title: 'Radio signal', type: ColumnType.text },
+      { property: 'scannerId', title: 'Scanner ID', type: ColumnType.text },
+      { property: 'timeStamp', title: 'End Time', type: ColumnType.text },
       {
-        property: "suspectIntervalFlag", title: "Suspect Interval", type: ColumnType.boolean
-      }
+        property: 'suspectIntervalFlag', title: 'Suspect Interval', type: ColumnType.boolean,
+      },
     ];
 
     chartPagedData.datasets.forEach(ds => {
@@ -83,67 +82,68 @@ class PerformanceDataComponent extends React.Component<PerformanceDataComponentP
     });
     return (
       <>
-        <ToggleContainer onToggleFilterButton={() => this.props.toggleFilterButton(!this.props.isFilterVisible)} existingFilter={this.props.existingFilter} onFilterChanged={this.onFilterChanged} selectedValue={this.props.currentView} showFilter={this.props.isFilterVisible} onChange={this.props.setSubView}>
+        <ToggleContainer onToggleFilterButton={() => this.props.toggleFilterButton(!this.props.isFilterVisible)}
+          existingFilter={this.props.existingFilter} onFilterChanged={this.onFilterChanged} selectedValue={this.props.currentView} showFilter={this.props.isFilterVisible} onChange={this.props.setSubView}>
           {lineChart(chartPagedData)}
-          <PerformanceDataTable stickyHeader idProperty={"_id"} tableId="performance-data-table" columns={performanceColumns} {...properties} {...actions} />
+          <PerformanceDataTable stickyHeader idProperty={'_id'} tableId="performance-data-table" columns={performanceColumns} {...properties} {...actions} />
         </ToggleContainer>
       </>
     );
-  };
+  }
 
   /**
    * This function gets the performance values for PerformanceData according on the chartjs dataset structure 
    * which is to be sent to the chart.
    */
   private getChartDataValues = (rows: PerformanceDataType[]): IDataSetsObject => {
-    const _rows = [...rows];
-    sortDataByTimeStamp(_rows);
+    const data_rows = [...rows];
+    sortDataByTimeStamp(data_rows);
 
     const datasets: IDataSet[] = [{
-      name: "es",
-      label: "es",
+      name: 'es',
+      label: 'es',
       borderColor: '#0e17f3de',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "ES"
+      columnLabel: 'ES',
     }, {
-      name: "ses",
-      label: "ses",
+      name: 'ses',
+      label: 'ses',
       borderColor: '#08edb6de',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "SES"
+      columnLabel: 'SES',
     }, {
-      name: "unavailability",
-      label: "unavailability",
+      name: 'unavailability',
+      label: 'unavailability',
       borderColor: '#b308edde',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "Unavailability"
+      columnLabel: 'Unavailability',
     }];
 
-    _rows.forEach(row => {
+    data_rows.forEach(row => {
       row.es = row.performanceData.es;
       row.ses = row.performanceData.ses;
       row.unavailability = row.performanceData.unavailability;
       datasets.forEach(ds => {
         ds.data.push({
-          x: row["timeStamp" as keyof PerformanceDataType] as string,
-          y: row.performanceData[ds.name as keyof PerformanceDatabaseDataType] as string
+          x: row['timeStamp' as keyof PerformanceDataType] as string,
+          y: row.performanceData[ds.name as keyof PerformanceDatabaseDataType] as string,
         });
       });
     });
 
     return {
-      datasets: datasets
+      datasets: datasets,
     };
-  }
+  };
 }
 
 const PerformanceData = withRouter(connect(mapProps, mapDisp)(PerformanceDataComponent));
index 8dc92b8..125cc6e 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
+import React from 'react';
+import { RouteComponentProps, withRouter } from 'react-router-dom';
 
-import { withRouter, RouteComponentProps } from 'react-router-dom';
-
-import { MaterialTable, ColumnType, ColumnModel, MaterialTableCtorType } from '../../../../framework/src/components/material-table';
+import { ColumnModel, ColumnType, MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table';
+import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect';
 import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
-import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect';
 
-import { ReceiveLevelDataType, ReceiveLevelDatabaseDataType } from '../models/receiveLevelDataType';
+import { SetFilterVisibility, SetSubViewAction } from '../actions/toggleActions';
+import { createReceiveLevelActions, createReceiveLevelProperties } from '../handlers/receiveLevelHandler';
 import { IDataSet, IDataSetsObject } from '../models/chartTypes';
-import { createReceiveLevelProperties, createReceiveLevelActions } from '../handlers/receiveLevelHandler';
+import { ReceiveLevelDatabaseDataType, ReceiveLevelDataType } from '../models/receiveLevelDataType';
 import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils';
 import { addColumnLabels } from '../utils/tableUtils';
-import { SetSubViewAction, SetFilterVisibility } from '../actions/toggleActions';
 import ToggleContainer from './toggleContainer';
 
 const mapProps = (state: IApplicationStoreState) => ({
   receiveLevelProperties: createReceiveLevelProperties(state),
   currentView: state.performanceHistory.subViews.receiveLevel.subView,
   isFilterVisible: state.performanceHistory.subViews.receiveLevel.isFilterVisible,
-  existingFilter: state.performanceHistory.receiveLevel.filter
+  existingFilter: state.performanceHistory.receiveLevel.filter,
 });
 
 const mapDisp = (dispatcher: IDispatcher) => ({
   receiveLevelActions: createReceiveLevelActions(dispatcher.dispatch),
-  setSubView: (value: "chart" | "table") => dispatcher.dispatch(new SetSubViewAction("receiveLevel", value)),
-  toggleFilterButton: (value: boolean) => { dispatcher.dispatch(new SetFilterVisibility("receiveLevel", value)) },
+  setSubView: (value: 'chart' | 'table') => dispatcher.dispatch(new SetSubViewAction('receiveLevel', value)),
+  toggleFilterButton: (value: boolean) => { dispatcher.dispatch(new SetFilterVisibility('receiveLevel', value)); },
 });
 
 type ReceiveLevelComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp> & {
-  selectedTimePeriod: string
+  selectedTimePeriod: string;
 };
 
 const ReceiveLevelTable = MaterialTable as MaterialTableCtorType<ReceiveLevelDataType>;
@@ -53,22 +52,21 @@ const ReceiveLevelTable = MaterialTable as MaterialTableCtorType<ReceiveLevelDat
 /**
  * The Component which gets the receiveLevel data from the database based on the selected time period.
  */
-class ReceiveLevelComponent extends React.Component<ReceiveLevelComponentProps>{
-
+class ReceiveLevelComponent extends React.Component<ReceiveLevelComponentProps> {
   onToggleFilterButton = () => {
     this.props.toggleFilterButton(!this.props.isFilterVisible);
-  }
+  };
 
 
-  onChange = (value: "chart" | "table") => {
+  onChange = (value: 'chart' | 'table') => {
     this.props.setSubView(value);
-  }
+  };
 
   onFilterChanged = (property: string, filterTerm: string) => {
     this.props.receiveLevelActions.onFilterChanged(property, filterTerm);
     if (!this.props.receiveLevelProperties.showFilter)
       this.props.receiveLevelActions.onToggleFilter(false);
-  }
+  };
 
   render(): JSX.Element {
     const properties = this.props.receiveLevelProperties;
@@ -76,12 +74,12 @@ class ReceiveLevelComponent extends React.Component<ReceiveLevelComponentProps>{
 
     const chartPagedData = this.getChartDataValues(properties.rows);
     const receiveLevelColumns: ColumnModel<ReceiveLevelDataType>[] = [
-      { property: "radioSignalId", title: "Radio signal", type: ColumnType.text },
-      { property: "scannerId", title: "Scanner ID", type: ColumnType.text },
-      { property: "timeStamp", title: "End Time", type: ColumnType.text },
+      { property: 'radioSignalId', title: 'Radio signal', type: ColumnType.text },
+      { property: 'scannerId', title: 'Scanner ID', type: ColumnType.text },
+      { property: 'timeStamp', title: 'End Time', type: ColumnType.text },
       {
-        property: "suspectIntervalFlag", title: "Suspect Interval", type: ColumnType.boolean
-      }
+        property: 'suspectIntervalFlag', title: 'Suspect Interval', type: ColumnType.boolean,
+      },
     ];
 
     chartPagedData.datasets.forEach(ds => {
@@ -92,64 +90,64 @@ class ReceiveLevelComponent extends React.Component<ReceiveLevelComponentProps>{
       <>
         <ToggleContainer onToggleFilterButton={this.onToggleFilterButton} showFilter={this.props.isFilterVisible} existingFilter={this.props.receiveLevelProperties.filter} onFilterChanged={this.onFilterChanged} selectedValue={this.props.currentView} onChange={this.onChange}>
           {lineChart(chartPagedData)}
-          <ReceiveLevelTable stickyHeader idProperty={"_id"} tableId="receive-level-table" columns={receiveLevelColumns} {...properties} {...actions} />
+          <ReceiveLevelTable stickyHeader idProperty={'_id'} tableId="receive-level-table" columns={receiveLevelColumns} {...properties} {...actions} />
         </ToggleContainer>
       </>
     );
-  };
+  }
 
   /**
    * This function gets the performance values for ReceiveLevel according on the chartjs dataset structure 
    * which is to be sent to the chart.
    */
   private getChartDataValues = (rows: ReceiveLevelDataType[]): IDataSetsObject => {
-    const _rows = [...rows];
-    sortDataByTimeStamp(_rows);
+    const data_rows = [...rows];
+    sortDataByTimeStamp(data_rows);
 
     const datasets: IDataSet[] = [{
-      name: "rxLevelMin",
-      label: "rx-level-min",
+      name: 'rxLevelMin',
+      label: 'rx-level-min',
       borderColor: '#0e17f3de',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "Rx min"
+      columnLabel: 'Rx min',
     }, {
-      name: "rxLevelAvg",
-      label: "rx-level-avg",
+      name: 'rxLevelAvg',
+      label: 'rx-level-avg',
       borderColor: '#08edb6de',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "Rx avg"
+      columnLabel: 'Rx avg',
     }, {
-      name: "rxLevelMax",
-      label: "rx-level-max",
+      name: 'rxLevelMax',
+      label: 'rx-level-max',
       borderColor: '#b308edde',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "Rx max"
+      columnLabel: 'Rx max',
     }];
 
-    _rows.forEach(row => {
+    data_rows.forEach(row => {
       row.rxLevelMin = row.performanceData.rxLevelMin;
       row.rxLevelAvg = row.performanceData.rxLevelAvg;
       row.rxLevelMax = row.performanceData.rxLevelMax;
       datasets.forEach(ds => {
         ds.data.push({
-          x: row["timeStamp" as keyof ReceiveLevelDataType] as string,
-          y: row.performanceData[ds.name as keyof ReceiveLevelDatabaseDataType] as string
+          x: row['timeStamp' as keyof ReceiveLevelDataType] as string,
+          y: row.performanceData[ds.name as keyof ReceiveLevelDatabaseDataType] as string,
         });
       });
     });
     return {
-      datasets: datasets
+      datasets: datasets,
     };
-  }
+  };
 }
 
 const ReceiveLevel = withRouter(connect(mapProps, mapDisp)(ReceiveLevelComponent));
index ee7fe34..85241fb 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
+import React from 'react';
+import { RouteComponentProps, withRouter } from 'react-router-dom';
 
-import { withRouter, RouteComponentProps } from 'react-router-dom';
-
-import { MaterialTable, ColumnType, ColumnModel, MaterialTableCtorType } from '../../../../framework/src/components/material-table';
+import { ColumnModel, ColumnType, MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table';
+import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect';
 import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
-import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect';
 
-import { SignalToInterferenceDataType, SignalToInterferenceDatabaseDataType } from '../models/signalToInteferenceDataType';
+import { SetFilterVisibility, SetSubViewAction } from '../actions/toggleActions';
+import { createSignalToInterferenceActions, createSignalToInterferenceProperties } from '../handlers/signalToInterferenceHandler';
 import { IDataSet, IDataSetsObject } from '../models/chartTypes';
-import { createSignalToInterferenceProperties, createSignalToInterferenceActions } from '../handlers/signalToInterferenceHandler';
+import { SignalToInterferenceDatabaseDataType, SignalToInterferenceDataType } from '../models/signalToInteferenceDataType';
 import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils';
 import { addColumnLabels } from '../utils/tableUtils';
-import { SetSubViewAction, SetFilterVisibility } from '../actions/toggleActions';
 import ToggleContainer from './toggleContainer';
 
 const mapProps = (state: IApplicationStoreState) => ({
   signalToInterferenceProperties: createSignalToInterferenceProperties(state),
   currentView: state.performanceHistory.subViews.SINR.subView,
   isFilterVisible: state.performanceHistory.subViews.SINR.isFilterVisible,
-  existingFilter: state.performanceHistory.signalToInterference.filter
+  existingFilter: state.performanceHistory.signalToInterference.filter,
 });
 
 const mapDisp = (dispatcher: IDispatcher) => ({
   signalToInterferenceActions: createSignalToInterferenceActions(dispatcher.dispatch),
-  setSubView: (value: "chart" | "table") => dispatcher.dispatch(new SetSubViewAction("SINR", value)),
-  toggleFilterButton: (value: boolean) => { dispatcher.dispatch(new SetFilterVisibility("SINR", value)) },
+  setSubView: (value: 'chart' | 'table') => dispatcher.dispatch(new SetSubViewAction('SINR', value)),
+  toggleFilterButton: (value: boolean) => { dispatcher.dispatch(new SetFilterVisibility('SINR', value)); },
 });
 
 type SignalToInterferenceComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp> & {
-  selectedTimePeriod: string
+  selectedTimePeriod: string;
 };
 
 const SignalToInterferenceTable = MaterialTable as MaterialTableCtorType<SignalToInterferenceDataType>;
@@ -53,21 +52,20 @@ const SignalToInterferenceTable = MaterialTable as MaterialTableCtorType<SignalT
 /**
  * The Component which gets the signal to interference data from the database based on the selected time period.
  */
-class SignalToInterferenceComponent extends React.Component<SignalToInterferenceComponentProps>{
-
+class SignalToInterferenceComponent extends React.Component<SignalToInterferenceComponentProps> {
   onToggleFilterButton = () => {
     this.props.toggleFilterButton(!this.props.isFilterVisible);
-  }
+  };
 
-  onChange = (value: "chart" | "table") => {
+  onChange = (value: 'chart' | 'table') => {
     this.props.setSubView(value);
-  }
+  };
 
   onFilterChanged = (property: string, filterTerm: string) => {
     this.props.signalToInterferenceActions.onFilterChanged(property, filterTerm);
     if (!this.props.signalToInterferenceProperties.showFilter)
       this.props.signalToInterferenceActions.onToggleFilter(false);
-  }
+  };
 
   render(): JSX.Element {
     const properties = this.props.signalToInterferenceProperties;
@@ -76,12 +74,12 @@ class SignalToInterferenceComponent extends React.Component<SignalToInterference
     const chartPagedData = this.getChartDataValues(properties.rows);
 
     const sinrColumns: ColumnModel<SignalToInterferenceDataType>[] = [
-      { property: "radioSignalId", title: "Radio signal", type: ColumnType.text },
-      { property: "scannerId", title: "Scanner ID", type: ColumnType.text },
-      { property: "timeStamp", title: "End Time", type: ColumnType.text },
+      { property: 'radioSignalId', title: 'Radio signal', type: ColumnType.text },
+      { property: 'scannerId', title: 'Scanner ID', type: ColumnType.text },
+      { property: 'timeStamp', title: 'End Time', type: ColumnType.text },
       {
-        property: "suspectIntervalFlag", title: "Suspect Interval", type: ColumnType.boolean
-      }
+        property: 'suspectIntervalFlag', title: 'Suspect Interval', type: ColumnType.boolean,
+      },
     ];
 
     chartPagedData.datasets.forEach(ds => {
@@ -89,14 +87,15 @@ class SignalToInterferenceComponent extends React.Component<SignalToInterference
     });
     return (
       <>
-        <ToggleContainer onToggleFilterButton={this.onToggleFilterButton} showFilter={this.props.isFilterVisible} existingFilter={this.props.signalToInterferenceProperties.filter} onFilterChanged={this.onFilterChanged} selectedValue={this.props.currentView} onChange={this.onChange}>
+        <ToggleContainer onToggleFilterButton={this.onToggleFilterButton} showFilter={this.props.isFilterVisible}
+          existingFilter={this.props.signalToInterferenceProperties.filter} onFilterChanged={this.onFilterChanged} selectedValue={this.props.currentView} onChange={this.onChange}>
           {lineChart(chartPagedData)}
-          <SignalToInterferenceTable stickyHeader idProperty={"_id"} tableId="signal-to-interference-table" columns={sinrColumns} {...properties} {...actions}
+          <SignalToInterferenceTable stickyHeader idProperty={'_id'} tableId="signal-to-interference-table" columns={sinrColumns} {...properties} {...actions}
           />
         </ToggleContainer>
       </>
     );
-  };
+  }
 
   /**
    * This function gets the performance values for SINR according on the chartjs dataset structure 
@@ -104,53 +103,53 @@ class SignalToInterferenceComponent extends React.Component<SignalToInterference
    */
 
   private getChartDataValues = (rows: SignalToInterferenceDataType[]): IDataSetsObject => {
-    const _rows = [...rows];
-    sortDataByTimeStamp(_rows);
+    const data_rows = [...rows];
+    sortDataByTimeStamp(data_rows);
 
     const datasets: IDataSet[] = [{
-      name: "snirMin",
-      label: "snir-min",
+      name: 'snirMin',
+      label: 'snir-min',
       borderColor: '#0e17f3de',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "SINR (min)[db]"
+      columnLabel: 'SINR (min)[db]',
     }, {
-      name: "snirAvg",
-      label: "snir-avg",
+      name: 'snirAvg',
+      label: 'snir-avg',
       borderColor: '#08edb6de',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "SINR (avg)[db]"
+      columnLabel: 'SINR (avg)[db]',
     }, {
-      name: "snirMax",
-      label: "snir-max",
+      name: 'snirMax',
+      label: 'snir-max',
       borderColor: '#b308edde',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "SINR (max)[db]"
+      columnLabel: 'SINR (max)[db]',
     }];
 
-    _rows.forEach(row => {
+    data_rows.forEach(row => {
       row.snirMin = row.performanceData.snirMin;
       row.snirAvg = row.performanceData.snirAvg;
       row.snirMax = row.performanceData.snirMax;
       datasets.forEach(ds => {
         ds.data.push({
-          x: row["timeStamp" as keyof SignalToInterferenceDataType] as string,
-          y: row.performanceData[ds.name as keyof SignalToInterferenceDatabaseDataType] as string
+          x: row['timeStamp' as keyof SignalToInterferenceDataType] as string,
+          y: row.performanceData[ds.name as keyof SignalToInterferenceDatabaseDataType] as string,
         });
       });
     });
     return {
-      datasets: datasets
+      datasets: datasets,
     };
-  }
+  };
 }
 
 const SignalToInterference = withRouter(connect(mapProps, mapDisp)(SignalToInterferenceComponent));
index 31e1d36..d4b8233 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
+import React from 'react';
+import { RouteComponentProps, withRouter } from 'react-router-dom';
 
-import { withRouter, RouteComponentProps } from 'react-router-dom';
-
-import { MaterialTable, ColumnType, ColumnModel, MaterialTableCtorType } from '../../../../framework/src/components/material-table';
+import { ColumnModel, ColumnType, MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table';
+import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect';
 import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
-import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect';
 
-import { TemperatureDataType, TemperatureDatabaseDataType } from '../models/temperatureDataType';
+import { SetFilterVisibility, SetSubViewAction } from '../actions/toggleActions';
+import { createTemperatureActions, createTemperatureProperties } from '../handlers/temperatureHandler';
 import { IDataSet, IDataSetsObject } from '../models/chartTypes';
-import { createTemperatureProperties, createTemperatureActions } from '../handlers/temperatureHandler';
+import { TemperatureDatabaseDataType, TemperatureDataType } from '../models/temperatureDataType';
 import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils';
 import { addColumnLabels } from '../utils/tableUtils';
 import ToggleContainer from './toggleContainer';
-import { SetSubViewAction, SetFilterVisibility } from '../actions/toggleActions';
 
 const mapProps = (state: IApplicationStoreState) => ({
   temperatureProperties: createTemperatureProperties(state),
   currentView: state.performanceHistory.subViews.temperatur.subView,
   isFilterVisible: state.performanceHistory.subViews.temperatur.isFilterVisible,
-  existingFilter: state.performanceHistory.temperature.filter
+  existingFilter: state.performanceHistory.temperature.filter,
 });
 
 const mapDisp = (dispatcher: IDispatcher) => ({
   temperatureActions: createTemperatureActions(dispatcher.dispatch),
-  setSubView: (value: "chart" | "table") => dispatcher.dispatch(new SetSubViewAction("Temp", value)),
-  toggleFilterButton: (value: boolean) => { dispatcher.dispatch(new SetFilterVisibility("Temp", value)) },
+  setSubView: (value: 'chart' | 'table') => dispatcher.dispatch(new SetSubViewAction('Temp', value)),
+  toggleFilterButton: (value: boolean) => { dispatcher.dispatch(new SetFilterVisibility('Temp', value)); },
 
 });
 
 type TemperatureComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp> & {
-  selectedTimePeriod: string
+  selectedTimePeriod: string;
 };
 
 const TemperatureTable = MaterialTable as MaterialTableCtorType<TemperatureDataType>;
@@ -54,22 +53,21 @@ const TemperatureTable = MaterialTable as MaterialTableCtorType<TemperatureDataT
 /**
  * The Component which gets the temperature data from the database based on the selected time period.
  */
-class TemperatureComponent extends React.Component<TemperatureComponentProps>{
-
+class TemperatureComponent extends React.Component<TemperatureComponentProps> {
   onToggleFilterButton = () => {
     this.props.toggleFilterButton(!this.props.isFilterVisible);
-  }
+  };
 
 
-  onChange = (value: "chart" | "table") => {
+  onChange = (value: 'chart' | 'table') => {
     this.props.setSubView(value);
-  }
+  };
 
   onFilterChanged = (property: string, filterTerm: string) => {
     this.props.temperatureActions.onFilterChanged(property, filterTerm);
     if (!this.props.temperatureProperties.showFilter)
       this.props.temperatureActions.onToggleFilter(false);
-  }
+  };
 
   render(): JSX.Element {
     const properties = this.props.temperatureProperties;
@@ -77,12 +75,12 @@ class TemperatureComponent extends React.Component<TemperatureComponentProps>{
 
     const chartPagedData = this.getChartDataValues(properties.rows);
     const temperatureColumns: ColumnModel<TemperatureDataType>[] = [
-      { property: "radioSignalId", title: "Radio signal", type: ColumnType.text },
-      { property: "scannerId", title: "Scanner ID", type: ColumnType.text },
-      { property: "timeStamp", title: "End Time", type: ColumnType.text },
+      { property: 'radioSignalId', title: 'Radio signal', type: ColumnType.text },
+      { property: 'scannerId', title: 'Scanner ID', type: ColumnType.text },
+      { property: 'timeStamp', title: 'End Time', type: ColumnType.text },
       {
-        property: "suspectIntervalFlag", title: "Suspect Interval", type: ColumnType.boolean
-      }
+        property: 'suspectIntervalFlag', title: 'Suspect Interval', type: ColumnType.boolean,
+      },
     ];
 
     chartPagedData.datasets.forEach(ds => {
@@ -93,11 +91,11 @@ class TemperatureComponent extends React.Component<TemperatureComponentProps>{
 
         <ToggleContainer onToggleFilterButton={this.onToggleFilterButton} showFilter={this.props.isFilterVisible} existingFilter={this.props.temperatureProperties.filter} onFilterChanged={this.onFilterChanged} selectedValue={this.props.currentView} onChange={this.onChange}>
           {lineChart(chartPagedData)}
-          <TemperatureTable stickyHeader idProperty={"_id"} tableId="temperature-table" columns={temperatureColumns} {...properties} {...actions} />
+          <TemperatureTable stickyHeader idProperty={'_id'} tableId="temperature-table" columns={temperatureColumns} {...properties} {...actions} />
         </ToggleContainer>
       </>
     );
-  };
+  }
 
   /**
    * This function gets the performance values for Temperature according on the chartjs dataset structure 
@@ -105,53 +103,53 @@ class TemperatureComponent extends React.Component<TemperatureComponentProps>{
    */
 
   private getChartDataValues = (rows: TemperatureDataType[]): IDataSetsObject => {
-    const _rows = [...rows];
-    sortDataByTimeStamp(_rows);
+    const data_rows = [...rows];
+    sortDataByTimeStamp(data_rows);
 
     const datasets: IDataSet[] = [{
-      name: "rfTempMin",
-      label: "rf-temp-min",
+      name: 'rfTempMin',
+      label: 'rf-temp-min',
       borderColor: '#0e17f3de',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "Rf Temp Min[deg C]"
+      columnLabel: 'Rf Temp Min[deg C]',
     }, {
-      name: "rfTempAvg",
-      label: "rf-temp-avg",
+      name: 'rfTempAvg',
+      label: 'rf-temp-avg',
       borderColor: '#08edb6de',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "Rf Temp Avg[deg C]"
+      columnLabel: 'Rf Temp Avg[deg C]',
     }, {
-      name: "rfTempMax",
-      label: "rf-temp-max",
+      name: 'rfTempMax',
+      label: 'rf-temp-max',
       borderColor: '#b308edde',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "Rf Temp Max[deg C]"
+      columnLabel: 'Rf Temp Max[deg C]',
     }];
 
-    _rows.forEach(row => {
+    data_rows.forEach(row => {
       row.rfTempMin = row.performanceData.rfTempMin;
       row.rfTempAvg = row.performanceData.rfTempAvg;
       row.rfTempMax = row.performanceData.rfTempMax;
       datasets.forEach(ds => {
         ds.data.push({
-          x: row["timeStamp" as keyof TemperatureDataType] as string,
-          y: row.performanceData[ds.name as keyof TemperatureDatabaseDataType] as string
+          x: row['timeStamp' as keyof TemperatureDataType] as string,
+          y: row.performanceData[ds.name as keyof TemperatureDatabaseDataType] as string,
         });
       });
     });
     return {
-      datasets: datasets
+      datasets: datasets,
     };
-  }
+  };
 }
 
 const Temperature = withRouter(connect(mapProps, mapDisp)(TemperatureComponent));
index 8696fe4..e883aef 100644 (file)
  */
 
 import * as React from 'react';
-import ToggleButton from '@mui/material/ToggleButton';
-import ToggleButtonGroup from '@mui/material/ToggleButtonGroup';
+
 import BarChartIcon from '@mui/icons-material/BarChart';
+import FilterListIcon from '@mui/icons-material/FilterList';
 import TableChartIcon from '@mui/icons-material/TableChart';
-import makeStyles from '@mui/styles/makeStyles';
+import ToggleButton from '@mui/material/ToggleButton';
+import ToggleButtonGroup from '@mui/material/ToggleButtonGroup';
 import Tooltip from '@mui/material/Tooltip';
-import ChartFilter from './chartFilter'
-import FilterListIcon from '@mui/icons-material/FilterList';
+import makeStyles from '@mui/styles/makeStyles';
+
+import ChartFilter from './chartFilter';
 
 const styles = makeStyles({
-    toggleButtonContainer: {
-        display: "flex",
-        alignItems: "center",
-        justifyContent: "center",
-        padding: "10px",
-    },
-    subViewGroup: {
-        padding: "10px"
-    },
-    filterGroup: {
-        marginLeft: "10px"
-    }
+  toggleButtonContainer: {
+    display: 'flex',
+    alignItems: 'center',
+    justifyContent: 'center',
+    padding: '10px',
+  },
+  subViewGroup: {
+    padding: '10px',
+  },
+  filterGroup: {
+    marginLeft: '10px',
+  },
 });
 
-type toggleProps = { selectedValue: string, onChange(value: string): void, showFilter: boolean, onToggleFilterButton(): void, onFilterChanged: (property: string, filterTerm: string) => void, existingFilter: any };
+type toggleProps = { selectedValue: string; onChange(value: string): void; showFilter: boolean; onToggleFilterButton(): void; onFilterChanged: (property: string, filterTerm: string) => void; existingFilter: any };
 
 const ToggleContainer: React.FunctionComponent<toggleProps> = (props) => {
 
-    const classes = styles();
+  const classes = styles();
 
-    const handleChange = (event: React.MouseEvent<HTMLElement>, newView: string) => {
-        if (newView !== null) {
-            props.onChange(newView)
-        }
-    };
+  const handleChange = (event: React.MouseEvent<HTMLElement>, newView: string) => {
+    if (newView !== null) {
+      props.onChange(newView);
+    }
+  };
 
-    const handleFilterChange = (event: React.MouseEvent<HTMLElement>, newView: string) => {
-        props.onToggleFilterButton();
-    };
+  const handleFilterChange = (_event: React.MouseEvent<HTMLElement>) => {
+    props.onToggleFilterButton();
+  };
 
-    const children = React.Children.toArray(props.children);
+  const children = React.Children.toArray(props.children);
 
-    //hide filter if visible + table
-    //put current name into state, let container handle stuff itelf, register for togglestate, get right via set name
+  //hide filter if visible + table
+  //put current name into state, let container handle stuff itelf, register for togglestate, get right via set name
 
-    return (
+  return (
         <>
             <div className={classes.toggleButtonContainer} >
                 <ToggleButtonGroup className={classes.subViewGroup} size="medium" value={props.selectedValue} exclusive onChange={handleChange}>
@@ -79,7 +81,7 @@ const ToggleContainer: React.FunctionComponent<toggleProps> = (props) => {
                 </ToggleButtonGroup>
 
                 <ToggleButtonGroup className={classes.filterGroup} onChange={handleFilterChange} >
-                    <ToggleButton value="" aria-label="show-filter" selected={props.showFilter as boolean} disabled={props.selectedValue !== "chart"}>
+                    <ToggleButton value="" aria-label="show-filter" selected={props.showFilter as boolean} disabled={props.selectedValue !== 'chart'}>
                         <Tooltip disableInteractive title={props.showFilter ? 'Hide filter' : 'Show available filter'}>
                             <FilterListIcon />
                         </Tooltip>
@@ -89,13 +91,12 @@ const ToggleContainer: React.FunctionComponent<toggleProps> = (props) => {
 
             </div>
             {
-                props.selectedValue === "chart" &&
+                props.selectedValue === 'chart' &&
                 <ChartFilter filters={props.existingFilter} onFilterChanged={props.onFilterChanged} isVisible={props.showFilter} />
 
             }
-            {props.selectedValue === "chart" ? children[0] : props.selectedValue === "table" && children[1]}
+            {props.selectedValue === 'chart' ? children[0] : props.selectedValue === 'table' && children[1]}
         </>);
-
-}
+};
 
 export default ToggleContainer;
\ No newline at end of file
index 97a35da..db9a7c0 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
+import React from 'react';
+import { RouteComponentProps, withRouter } from 'react-router-dom';
 
-import { withRouter, RouteComponentProps } from 'react-router-dom';
-
-import { MaterialTable, ColumnType, ColumnModel, MaterialTableCtorType } from '../../../../framework/src/components/material-table';
+import { ColumnModel, ColumnType, MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table';
+import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect';
 import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
-import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect';
 
-import { TransmissionPowerDataType, TransmissionPowerDatabaseDataType } from '../models/transmissionPowerDataType';
+import { SetFilterVisibility, SetSubViewAction } from '../actions/toggleActions';
+import { createTransmissionPowerActions, createTransmissionPowerProperties } from '../handlers/transmissionPowerHandler';
 import { IDataSet, IDataSetsObject } from '../models/chartTypes';
-import { createTransmissionPowerProperties, createTransmissionPowerActions } from '../handlers/transmissionPowerHandler';
+import { TransmissionPowerDatabaseDataType, TransmissionPowerDataType } from '../models/transmissionPowerDataType';
 import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils';
 import { addColumnLabels } from '../utils/tableUtils';
-import { SetSubViewAction, SetFilterVisibility } from '../actions/toggleActions';
 import ToggleContainer from './toggleContainer';
 
 const mapProps = (state: IApplicationStoreState) => ({
   transmissionPowerProperties: createTransmissionPowerProperties(state),
   currentView: state.performanceHistory.subViews.transmissionPower.subView,
   isFilterVisible: state.performanceHistory.subViews.transmissionPower.isFilterVisible,
-  existingFilter: state.performanceHistory.transmissionPower.filter
+  existingFilter: state.performanceHistory.transmissionPower.filter,
 });
 
 const mapDisp = (dispatcher: IDispatcher) => ({
   transmissionPowerActions: createTransmissionPowerActions(dispatcher.dispatch),
-  setSubView: (value: "chart" | "table") => dispatcher.dispatch(new SetSubViewAction("transmissionPower", value)),
-  toggleFilterButton: (value: boolean) => { dispatcher.dispatch(new SetFilterVisibility("transmissionPower", value)) },
+  setSubView: (value: 'chart' | 'table') => dispatcher.dispatch(new SetSubViewAction('transmissionPower', value)),
+  toggleFilterButton: (value: boolean) => { dispatcher.dispatch(new SetFilterVisibility('transmissionPower', value)); },
 
 
 });
 
 type TransmissionPowerComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp> & {
-  selectedTimePeriod: string
-}
+  selectedTimePeriod: string;
+};
 
 const TransmissionPowerTable = MaterialTable as MaterialTableCtorType<TransmissionPowerDataType>;
 
 /**
  * The Component which gets the transmission power data from the database based on the selected time period.
  */
-class TransmissionPowerComponent extends React.Component<TransmissionPowerComponentProps>{
-
+class TransmissionPowerComponent extends React.Component<TransmissionPowerComponentProps> {
   onToggleFilterButton = () => {
     this.props.toggleFilterButton(!this.props.isFilterVisible);
-  }
+  };
 
-  onChange = (value: "chart" | "table") => {
+  onChange = (value: 'chart' | 'table') => {
     this.props.setSubView(value);
-  }
+  };
 
   onFilterChanged = (property: string, filterTerm: string) => {
     this.props.transmissionPowerActions.onFilterChanged(property, filterTerm);
     if (!this.props.transmissionPowerProperties.showFilter)
       this.props.transmissionPowerActions.onToggleFilter(false);
-  }
+  };
 
   render(): JSX.Element {
-    const properties = this.props.transmissionPowerProperties
-    const actions = this.props.transmissionPowerActions
+    const properties = this.props.transmissionPowerProperties;
+    const actions = this.props.transmissionPowerActions;
 
     const chartPagedData = this.getChartDataValues(properties.rows);
 
     const transmissionColumns: ColumnModel<TransmissionPowerDataType>[] = [
-      { property: "radioSignalId", title: "Radio signal", type: ColumnType.text },
-      { property: "scannerId", title: "Scanner ID", type: ColumnType.text },
-      { property: "timeStamp", title: "End Time", type: ColumnType.text },
+      { property: 'radioSignalId', title: 'Radio signal', type: ColumnType.text },
+      { property: 'scannerId', title: 'Scanner ID', type: ColumnType.text },
+      { property: 'timeStamp', title: 'End Time', type: ColumnType.text },
       {
-        property: "suspectIntervalFlag", title: "Suspect Interval", type: ColumnType.boolean
-      }
+        property: 'suspectIntervalFlag', title: 'Suspect Interval', type: ColumnType.boolean,
+      },
     ];
 
     chartPagedData.datasets.forEach(ds => {
@@ -92,13 +90,14 @@ class TransmissionPowerComponent extends React.Component<TransmissionPowerCompon
 
     return (
       <>
-        <ToggleContainer onChange={this.onChange} onToggleFilterButton={this.onToggleFilterButton} showFilter={this.props.isFilterVisible} existingFilter={this.props.transmissionPowerProperties.filter} onFilterChanged={this.onFilterChanged} selectedValue={this.props.currentView} >
+        <ToggleContainer onChange={this.onChange} onToggleFilterButton={this.onToggleFilterButton} showFilter={this.props.isFilterVisible}
+          existingFilter={this.props.transmissionPowerProperties.filter} onFilterChanged={this.onFilterChanged} selectedValue={this.props.currentView} >
           {lineChart(chartPagedData)}
-          <TransmissionPowerTable stickyHeader idProperty={"_id"} tableId="transmission-power-table" columns={transmissionColumns} {...properties} {...actions} />
+          <TransmissionPowerTable stickyHeader idProperty={'_id'} tableId="transmission-power-table" columns={transmissionColumns} {...properties} {...actions} />
         </ToggleContainer>
       </>
     );
-  };
+  }
 
   /**
    * This function gets the performance values for TransmissionPower according on the chartjs dataset structure 
@@ -106,53 +105,53 @@ class TransmissionPowerComponent extends React.Component<TransmissionPowerCompon
    */
 
   private getChartDataValues = (rows: TransmissionPowerDataType[]): IDataSetsObject => {
-    const _rows = [...rows];
-    sortDataByTimeStamp(_rows);
+    const data_rows = [...rows];
+    sortDataByTimeStamp(data_rows);
 
     const datasets: IDataSet[] = [{
-      name: "txLevelMin",
-      label: "tx-level-min",
+      name: 'txLevelMin',
+      label: 'tx-level-min',
       borderColor: '#0e17f3de',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "Tx min"
+      columnLabel: 'Tx min',
     }, {
-      name: "txLevelAvg",
-      label: "tx-level-avg",
+      name: 'txLevelAvg',
+      label: 'tx-level-avg',
       borderColor: '#08edb6de',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "Tx avg"
+      columnLabel: 'Tx avg',
     }, {
-      name: "txLevelMax",
-      label: "tx-level-max",
+      name: 'txLevelMax',
+      label: 'tx-level-max',
       borderColor: '#b308edde',
       bezierCurve: false,
       lineTension: 0,
       fill: false,
       data: [],
-      columnLabel: "Tx max"
+      columnLabel: 'Tx max',
     }];
 
-    _rows.forEach(row => {
+    data_rows.forEach(row => {
       row.txLevelMin = row.performanceData.txLevelMin;
       row.txLevelAvg = row.performanceData.txLevelAvg;
       row.txLevelMax = row.performanceData.txLevelMax;
       datasets.forEach(ds => {
         ds.data.push({
-          x: row["timeStamp" as keyof TransmissionPowerDataType] as string,
-          y: row.performanceData[ds.name as keyof TransmissionPowerDatabaseDataType] as string
+          x: row['timeStamp' as keyof TransmissionPowerDataType] as string,
+          y: row.performanceData[ds.name as keyof TransmissionPowerDatabaseDataType] as string,
         });
       });
     });
     return {
-      datasets: datasets
+      datasets: datasets,
     };
-  }
+  };
 }
 
 const TransmissionPower = withRouter(connect(mapProps, mapDisp)(TransmissionPowerComponent));
index 8857845..9baf545 100644 (file)
@@ -26,12 +26,12 @@ export interface IAdaptiveModulationState extends IExternalTableState<AdaptiveMo
 /**
  * Creates elastic search material data fetch handler for Adaptive modulation from historicalperformance database.
  */
-const adaptiveModulationSearchHandler = createSearchDataHandler<AdaptiveModulationDataType>(getFilter, false, null)
+const adaptiveModulationSearchHandler = createSearchDataHandler<AdaptiveModulationDataType>(getFilter, false, null);
 export const {
-    actionHandler: adaptiveModulationActionHandler,
-    createActions: createAdaptiveModulationActions,
-    createProperties: createAdaptiveModulationProperties,
-    createPreActions: createAdaptiveModulationPreActions,
-    reloadAction: adaptiveModulationReloadAction,
+  actionHandler: adaptiveModulationActionHandler,
+  createActions: createAdaptiveModulationActions,
+  createProperties: createAdaptiveModulationProperties,
+  createPreActions: createAdaptiveModulationPreActions,
+  reloadAction: adaptiveModulationReloadAction,
 } = createExternal<AdaptiveModulationDataType>(adaptiveModulationSearchHandler, appState => appState.performanceHistory.adaptiveModulation);
 
index 4605efd..f943ef4 100644 (file)
@@ -38,7 +38,7 @@ const ltpListStateInit: IAvailableLtpsState = {
   distinctLtps: [],
   busy: false,
   loadedOnce: false,
-  error: undefined
+  error: undefined,
 };
 
 export const availableLtpsActionHandler: IActionHandler<IAvailableLtpsState> = (state = ltpListStateInit, action) => {
@@ -46,7 +46,7 @@ export const availableLtpsActionHandler: IActionHandler<IAvailableLtpsState> = (
 
     state = {
       ...state,
-      busy: true
+      busy: true,
     };
 
   } else if (action instanceof AllAvailableLtpsLoadedAction) {
@@ -56,21 +56,21 @@ export const availableLtpsActionHandler: IActionHandler<IAvailableLtpsState> = (
         distinctLtps: action.availableLtps,
         busy: false,
         error: undefined,
-        loadedOnce: true
+        loadedOnce: true,
       };
     } else if (action.error) {
       state = {
         ...state,
         busy: false,
         loadedOnce: true,
-        error: action.error
-      }
+        error: action.error,
+      };
     }
   } else if (action instanceof SetInitialLoadedAction) {
 
     state = {
       ...state,
-      loadedOnce: action.initialLoaded
+      loadedOnce: action.initialLoaded,
     };
   } else if (action instanceof NoLtpsFoundAction) {
     state = {
@@ -78,22 +78,20 @@ export const availableLtpsActionHandler: IActionHandler<IAvailableLtpsState> = (
       busy: false,
       error: undefined,
       loadedOnce: true,
-      distinctLtps: []
-    }
+      distinctLtps: [],
+    };
   } else if (action instanceof ResetLtpsAction) {
     state = {
       ...state,
       busy: false,
       error: undefined,
       loadedOnce: false,
-      distinctLtps: []
-    }
-  }
-
-  else {
+      distinctLtps: [],
+    };
+  } else {
     state = {
       ...state,
-      busy: false
+      busy: false,
     };
   }
 
index 5008dd5..96cabfa 100644 (file)
@@ -26,13 +26,13 @@ export interface ICrossPolarDiscriminationState extends IExternalTableState<Cros
 /**
  * Creates elastic search material data fetch handler for CPD from historicalperformance database.
  */
-const crossPolarDiscriminationSearchHandler = createSearchDataHandler<CrossPolarDiscriminationDataType>(getFilter, false, null)
+const crossPolarDiscriminationSearchHandler = createSearchDataHandler<CrossPolarDiscriminationDataType>(getFilter, false, null);
 
 export const {
-    actionHandler: crossPolarDiscriminationActionHandler,
-    createActions: createCrossPolarDiscriminationActions,
-    createProperties: createCrossPolarDiscriminationProperties,
-    createPreActions: createCrossPolarDiscriminationPreActions,
-    reloadAction: crossPolarDiscriminationReloadAction,
+  actionHandler: crossPolarDiscriminationActionHandler,
+  createActions: createCrossPolarDiscriminationActions,
+  createProperties: createCrossPolarDiscriminationProperties,
+  createPreActions: createCrossPolarDiscriminationPreActions,
+  reloadAction: crossPolarDiscriminationReloadAction,
 } = createExternal<CrossPolarDiscriminationDataType>(crossPolarDiscriminationSearchHandler, appState => appState.performanceHistory.crossPolarDiscrimination);
 
index b3c0db4..11e380a 100644 (file)
@@ -27,7 +27,7 @@ export interface IDeviceListState {
 
 const deviceListStateInit: IDeviceListState = {
   deviceList: [],
-  busy: false
+  busy: false,
 };
 
 export const deviceListActionHandler: IActionHandler<IDeviceListState> = (state = deviceListStateInit, action) => {
@@ -35,7 +35,7 @@ export const deviceListActionHandler: IActionHandler<IDeviceListState> = (state
 
     state = {
       ...state,
-      busy: true
+      busy: true,
     };
 
   } else if (action instanceof AllDeviceListLoadedAction) {
@@ -43,12 +43,12 @@ export const deviceListActionHandler: IActionHandler<IDeviceListState> = (state
       state = {
         ...state,
         deviceList: action.deviceList,
-        busy: false
+        busy: false,
       };
     } else {
       state = {
         ...state,
-        busy: false
+        busy: false,
       };
     }
   }
index a7b63a3..664c7cd 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as moment from 'moment';
 
 import { createExternal, IExternalTableState } from '../../../../framework/src/components/material-table/utilities';
 import { createSearchDataHandler } from '../../../../framework/src/utilities/elasticSearch';
 
-import {  PerformanceDataType } from '../models/performanceDataType';
+import { PerformanceDataType } from '../models/performanceDataType';
 import { getFilter } from '../utils/tableUtils';
 
 export interface IPerformanceDataState extends IExternalTableState<PerformanceDataType> { }
@@ -31,10 +30,10 @@ export interface IPerformanceDataState extends IExternalTableState<PerformanceDa
 const performanceDataSearchHandler = createSearchDataHandler<PerformanceDataType>(getFilter, false, null);
 
 export const {
-    actionHandler: performanceDataActionHandler,
-    createActions: createPerformanceDataActions,
-    createProperties: createPerformanceDataProperties,
-    createPreActions: createPerformanceDataPreActions,
-    reloadAction: performanceDataReloadAction
+  actionHandler: performanceDataActionHandler,
+  createActions: createPerformanceDataActions,
+  createProperties: createPerformanceDataProperties,
+  createPreActions: createPerformanceDataPreActions,
+  reloadAction: performanceDataReloadAction,
 } = createExternal<PerformanceDataType>(performanceDataSearchHandler, appState => appState.performanceHistory.performanceData);
 
index a05ff37..c0cee46 100644 (file)
  */
 // main state handler
 
+import { IActionHandler } from '../../../../framework/src/flux/action';
 import { combineActionHandler } from '../../../../framework/src/flux/middleware';
-
 // ** do not remove **
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
 import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
-import { IActionHandler } from '../../../../framework/src/flux/action';
 
 import { IConnectAppStoreState } from '../../../connectApp/src/handlers/connectAppRootHandler';
+import { UpdateMountId } from '../actions/deviceListActions';
+import { SetPanelAction } from '../actions/panelChangeActions';
+import { ReloadAction } from '../actions/reloadAction';
+import { TimeChangeAction } from '../actions/timeChangeAction';
+import { ResetAllSubViewsAction, SetFilterVisibility, SetSubViewAction } from '../actions/toggleActions';
+import { PmDataInterval } from '../models/performanceDataType';
+import { currentViewType, SubTabType } from '../models/toggleDataType';
+import { adaptiveModulationActionHandler, IAdaptiveModulationState } from './adaptiveModulationHandler';
+import { availableLtpsActionHandler, IAvailableLtpsState } from './availableLtpsActionHandler';
+import { crossPolarDiscriminationActionHandler, ICrossPolarDiscriminationState } from './crossPolarDiscriminationHandler';
+import { deviceListActionHandler, IDeviceListState } from './deviceListActionHandler';
 import { IPerformanceDataState, performanceDataActionHandler } from './performanceDataHandler';
 import { IReceiveLevelState, receiveLevelActionHandler } from './receiveLevelHandler';
-import { ITransmissionPowerState, transmissionPowerActionHandler } from './transmissionPowerHandler';
-import { IAdaptiveModulationState, adaptiveModulationActionHandler } from './adaptiveModulationHandler';
-import { ITemperatureState, temperatureActionHandler } from './temperatureHandler';
 import { ISignalToInterferenceState, signalToInterferenceActionHandler } from './signalToInterferenceHandler';
-import { ICrossPolarDiscriminationState, crossPolarDiscriminationActionHandler } from './crossPolarDiscriminationHandler';
-import { SetPanelAction } from '../actions/panelChangeActions';
-import { IDeviceListState, deviceListActionHandler } from './deviceListActionHandler';
-import { IAvailableLtpsState, availableLtpsActionHandler } from './availableLtpsActionHandler';
-import { PmDataInterval } from '../models/performanceDataType';
-import { TimeChangeAction } from '../actions/timeChangeAction';
-import { UpdateMountId } from '../actions/deviceListActions';
-import { SetSubViewAction, ResetAllSubViewsAction, SetFilterVisibility } from '../actions/toggleActions';
-import { SubTabType, currentViewType } from '../models/toggleDataType';
-import { ReloadAction } from '../actions/reloadAction';
+import { ITemperatureState, temperatureActionHandler } from './temperatureHandler';
+import { ITransmissionPowerState, transmissionPowerActionHandler } from './transmissionPowerHandler';
 
 export interface IPerformanceHistoryStoreState {
   nodeId: string;
@@ -58,15 +58,15 @@ export interface IPerformanceHistoryStoreState {
   isReloadSchedueled: boolean;
 }
 
-const mountIdHandler: IActionHandler<string> = (state = "", action) => {
+const mountIdHandler: IActionHandler<string> = (state = '', action) => {
   if (action instanceof UpdateMountId) {
-    state = "";
+    state = '';
     if (action.nodeId) {
       state = action.nodeId;
     }
   }
   return state;
-}
+};
 
 const reloadHandler: IActionHandler<boolean> = (state = false, action) => {
 
@@ -74,7 +74,7 @@ const reloadHandler: IActionHandler<boolean> = (state = false, action) => {
     state = action.show;
   }
   return state;
-}
+};
 
 
 const currentOpenPanelHandler: IActionHandler<string | null> = (state = null, action) => {
@@ -82,59 +82,75 @@ const currentOpenPanelHandler: IActionHandler<string | null> = (state = null, ac
     state = action.panelId;
   }
   return state;
-}
+};
 
 const currentPMDataIntervalHandler: IActionHandler<PmDataInterval> = (state = PmDataInterval.pmInterval15Min, action) => {
   if (action instanceof TimeChangeAction) {
     state = action.time;
   }
   return state;
-}
+};
 
-type filterableSubview = { subView: SubTabType, isFilterVisible: boolean };
-type toggleViewDataType = { currentSubView: currentViewType, performanceData: filterableSubview, receiveLevel: filterableSubview, transmissionPower: filterableSubview, adaptiveModulation: filterableSubview, temperatur: filterableSubview, SINR: filterableSubview, CPD: filterableSubview };
+type filterableSubview = { subView: SubTabType; isFilterVisible: boolean };
+type toggleViewDataType = {
+  currentSubView: currentViewType;
+  performanceData: filterableSubview;
+  receiveLevel: filterableSubview;
+  transmissionPower: filterableSubview;
+  adaptiveModulation: filterableSubview;
+  temperatur: filterableSubview;
+  SINR: filterableSubview;
+  CPD: filterableSubview;
+};
 
 
-const toogleViewDataHandler: IActionHandler<toggleViewDataType> = (state = { currentSubView: "performanceData", performanceData: { subView: "chart", isFilterVisible: true }, receiveLevel: { subView: "chart", isFilterVisible: true }, adaptiveModulation: { subView: "chart", isFilterVisible: true }, transmissionPower: { subView: "chart", isFilterVisible: true }, temperatur: { subView: "chart", isFilterVisible: true }, SINR: { subView: "chart", isFilterVisible: true }, CPD: { subView: "chart", isFilterVisible: true } }, action) => {
+const toogleViewDataHandler: IActionHandler<toggleViewDataType> = (
+  state = {
+    currentSubView: 'performanceData',
+    performanceData: { subView: 'chart', isFilterVisible: true },
+    receiveLevel: { subView: 'chart', isFilterVisible: true },
+    adaptiveModulation: { subView: 'chart', isFilterVisible: true },
+    transmissionPower: { subView: 'chart', isFilterVisible: true },
+    temperatur: { subView: 'chart', isFilterVisible: true },
+    SINR: { subView: 'chart', isFilterVisible: true },
+    CPD: { subView: 'chart', isFilterVisible: true },
+  }, action) => {
 
   if (action instanceof SetSubViewAction) {
     switch (action.currentView) {
-      case "performanceData": state = { ...state, performanceData: { ...state.performanceData, subView: action.selectedTab } }; break;
-      case "adaptiveModulation": state = { ...state, adaptiveModulation: { ...state.adaptiveModulation, subView: action.selectedTab } }; break;
-      case "receiveLevel": state = { ...state, receiveLevel: { ...state.receiveLevel, subView: action.selectedTab } }; break;
-      case "transmissionPower": state = { ...state, transmissionPower: { ...state.transmissionPower, subView: action.selectedTab } }; break;
-      case "Temp": state = { ...state, temperatur: { ...state.temperatur, subView: action.selectedTab } }; break;
-      case "SINR": state = { ...state, SINR: { ...state.SINR, subView: action.selectedTab } }; break;
-      case "CPD": state = { ...state, CPD: { ...state.CPD, subView: action.selectedTab } }; break;
+      case 'performanceData': state = { ...state, performanceData: { ...state.performanceData, subView: action.selectedTab } }; break;
+      case 'adaptiveModulation': state = { ...state, adaptiveModulation: { ...state.adaptiveModulation, subView: action.selectedTab } }; break;
+      case 'receiveLevel': state = { ...state, receiveLevel: { ...state.receiveLevel, subView: action.selectedTab } }; break;
+      case 'transmissionPower': state = { ...state, transmissionPower: { ...state.transmissionPower, subView: action.selectedTab } }; break;
+      case 'Temp': state = { ...state, temperatur: { ...state.temperatur, subView: action.selectedTab } }; break;
+      case 'SINR': state = { ...state, SINR: { ...state.SINR, subView: action.selectedTab } }; break;
+      case 'CPD': state = { ...state, CPD: { ...state.CPD, subView: action.selectedTab } }; break;
     }
-  }
-  else if (action instanceof SetFilterVisibility) {
+  } else if (action instanceof SetFilterVisibility) {
     switch (action.currentView) {
-      case "performanceData": state = {
-        ...state, performanceData: { ...state.performanceData, isFilterVisible: action.isVisible }
+      case 'performanceData': state = {
+        ...state, performanceData: { ...state.performanceData, isFilterVisible: action.isVisible },
       }; break;
-      case "adaptiveModulation": state = { ...state, adaptiveModulation: { ...state.performanceData, isFilterVisible: action.isVisible } }; break;
-      case "receiveLevel": state = { ...state, receiveLevel: { ...state.receiveLevel, isFilterVisible: action.isVisible } }; break;
-      case "transmissionPower": state = { ...state, transmissionPower: { ...state.transmissionPower, isFilterVisible: action.isVisible } }; break;
-      case "Temp": state = { ...state, temperatur: { ...state.temperatur, isFilterVisible: action.isVisible } }; break;
-      case "SINR": state = { ...state, SINR: { ...state.SINR, isFilterVisible: action.isVisible } }; break;
-      case "CPD": state = { ...state, CPD: { ...state.CPD, isFilterVisible: action.isVisible } }; break;
+      case 'adaptiveModulation': state = { ...state, adaptiveModulation: { ...state.performanceData, isFilterVisible: action.isVisible } }; break;
+      case 'receiveLevel': state = { ...state, receiveLevel: { ...state.receiveLevel, isFilterVisible: action.isVisible } }; break;
+      case 'transmissionPower': state = { ...state, transmissionPower: { ...state.transmissionPower, isFilterVisible: action.isVisible } }; break;
+      case 'Temp': state = { ...state, temperatur: { ...state.temperatur, isFilterVisible: action.isVisible } }; break;
+      case 'SINR': state = { ...state, SINR: { ...state.SINR, isFilterVisible: action.isVisible } }; break;
+      case 'CPD': state = { ...state, CPD: { ...state.CPD, isFilterVisible: action.isVisible } }; break;
     }
-
   } else if (action instanceof ResetAllSubViewsAction) {
     state = {
-      ...state, performanceData: { ...state.performanceData, subView: "chart" },
-      adaptiveModulation: { ...state.adaptiveModulation, subView: "chart" },
-      receiveLevel: { ...state.receiveLevel, subView: "chart" },
-      transmissionPower: { ...state.transmissionPower, subView: "chart" },
-      temperatur: { ...state.temperatur, subView: "chart" },
-      SINR: { ...state.SINR, subView: "chart" },
-      CPD: { ...state.CPD, subView: "chart" }
-    }
+      ...state, performanceData: { ...state.performanceData, subView: 'chart' },
+      adaptiveModulation: { ...state.adaptiveModulation, subView: 'chart' },
+      receiveLevel: { ...state.receiveLevel, subView: 'chart' },
+      transmissionPower: { ...state.transmissionPower, subView: 'chart' },
+      temperatur: { ...state.temperatur, subView: 'chart' },
+      SINR: { ...state.SINR, subView: 'chart' },
+      CPD: { ...state.CPD, subView: 'chart' },
+    };
   }
-
   return state;
-}
+};
 
 declare module '../../../../framework/src/store/applicationStore' {
   interface IApplicationStoreState {
@@ -157,7 +173,7 @@ const actionHandlers = {
   currentOpenPanel: currentOpenPanelHandler,
   pmDataIntervalType: currentPMDataIntervalHandler,
   subViews: toogleViewDataHandler,
-  isReloadSchedueled: reloadHandler
+  isReloadSchedueled: reloadHandler,
 };
 
 const performanceHistoryRootHandler = combineActionHandler<IPerformanceHistoryStoreState>(actionHandlers);
index 34fd3eb..78626eb 100644 (file)
@@ -29,10 +29,10 @@ export interface IReceiveLevelState extends IExternalTableState<ReceiveLevelData
 const receiveLevelSearchHandler = createSearchDataHandler<ReceiveLevelDataType>(getFilter, false, null);
 
 export const {
-    actionHandler: receiveLevelActionHandler,
-    createActions: createReceiveLevelActions,
-    createProperties: createReceiveLevelProperties,
-    createPreActions: createReceiveLevelPreActions,
-    reloadAction: receiveLevelReloadAction,
+  actionHandler: receiveLevelActionHandler,
+  createActions: createReceiveLevelActions,
+  createProperties: createReceiveLevelProperties,
+  createPreActions: createReceiveLevelPreActions,
+  reloadAction: receiveLevelReloadAction,
 } = createExternal<ReceiveLevelDataType>(receiveLevelSearchHandler, appState => appState.performanceHistory.receiveLevel);
 
index 5fd5c0c..ab6efab 100644 (file)
@@ -29,10 +29,10 @@ export interface ISignalToInterferenceState extends IExternalTableState<SignalTo
 const signalToInterferenceSearchHandler = createSearchDataHandler<SignalToInterferenceDataType>(getFilter, false, null);
 
 export const {
-    actionHandler: signalToInterferenceActionHandler,
-    createActions: createSignalToInterferenceActions,
-    createProperties: createSignalToInterferenceProperties,
-    createPreActions: createSignalToInterferencePreActions,
-    reloadAction: signalToInterferenceReloadAction,
+  actionHandler: signalToInterferenceActionHandler,
+  createActions: createSignalToInterferenceActions,
+  createProperties: createSignalToInterferenceProperties,
+  createPreActions: createSignalToInterferencePreActions,
+  reloadAction: signalToInterferenceReloadAction,
 } = createExternal<SignalToInterferenceDataType>(signalToInterferenceSearchHandler, appState => appState.performanceHistory.signalToInterference);
 
index 130b818..02bf69b 100644 (file)
@@ -26,13 +26,13 @@ export interface ITemperatureState extends IExternalTableState<TemperatureDataTy
 /**
  * Creates elastic search material data fetch handler for Temperature from historicalperformance database.
  */
-const temperatureSearchHandler = createSearchDataHandler< TemperatureDataType>(getFilter, false, null);
+const temperatureSearchHandler = createSearchDataHandler<TemperatureDataType>(getFilter, false, null);
 
 export const {
-    actionHandler: temperatureActionHandler,
-    createActions: createTemperatureActions,
-    createProperties: createTemperatureProperties,
-    createPreActions: createTemperaturePreActions,
-    reloadAction: temperatureReloadAction,
+  actionHandler: temperatureActionHandler,
+  createActions: createTemperatureActions,
+  createProperties: createTemperatureProperties,
+  createPreActions: createTemperaturePreActions,
+  reloadAction: temperatureReloadAction,
 } = createExternal<TemperatureDataType>(temperatureSearchHandler, appState => appState.performanceHistory.temperature);
 
index 2a09e58..9cf70dc 100644 (file)
@@ -26,13 +26,13 @@ export interface ITransmissionPowerState extends IExternalTableState<Transmissio
 /**
  * Creates elastic search material data fetch handler for Transmission power from historicalperformance database.
  */
-const transmissionPowerSearchHandler = createSearchDataHandler<TransmissionPowerDataType>(getFilter, false, null)
+const transmissionPowerSearchHandler = createSearchDataHandler<TransmissionPowerDataType>(getFilter, false, null);
 
 export const {
-    actionHandler: transmissionPowerActionHandler,
-    createActions: createTransmissionPowerActions,
-    createProperties: createTransmissionPowerProperties,
-    createPreActions: createTransmissionPowerPreActions,
-    reloadAction: transmissionPowerReloadAction,
+  actionHandler: transmissionPowerActionHandler,
+  createActions: createTransmissionPowerActions,
+  createProperties: createTransmissionPowerProperties,
+  createPreActions: createTransmissionPowerPreActions,
+  reloadAction: transmissionPowerReloadAction,
 } = createExternal<TransmissionPowerDataType>(transmissionPowerSearchHandler, appState => appState.performanceHistory.transmissionPower);
 
index dc6c7bc..6006157 100644 (file)
@@ -15,7 +15,7 @@
  * the License.
  * ============LICENSE_END==========================================================================
  */
-  export type LtpIds = {
-    key : string
-  }
+export type LtpIds = {
+  key: string;
+};
 
index 53039fa..969c0b3 100644 (file)
@@ -24,21 +24,21 @@ export interface IData {
  * Structure of chartjs dataset with the chart properties.
  */
 export interface IDataSet {
-  name: string,
-  label: string,
-  lineTension: 0,
+  name: string;
+  label: string;
+  lineTension: 0;
   bezierCurve: boolean;
-  fill: boolean,
-  borderColor: string,
-  data: IData[],
-  columnLabel: string
+  fill: boolean;
+  borderColor: string;
+  data: IData[];
+  columnLabel: string;
 }
 
 /**
  * Structure of chartjs dataset which is sent to the chart.
  */
 export interface IDataSetsObject {
-  datasets: IDataSet[]
+  datasets: IDataSet[];
 }
 
 /**
index 8adb16f..749624b 100644 (file)
@@ -32,7 +32,7 @@ export type CrossPolarDiscriminationDatabaseDataType = {
  * Internally used type to provide table and chart data
  */
 export type CrossPolarDiscriminationDataType = {
-  performanceData: CrossPolarDiscriminationDatabaseDataType
+  performanceData: CrossPolarDiscriminationDatabaseDataType;
   radioSignalId: string;
   scannerId: string;
   timeStamp: string;
index 5889a64..08bf7f8 100644 (file)
@@ -19,4 +19,4 @@
 /**
  * Represents PanelIds for the available Expansional panels.
  */
-export type PanelId = null | "PerformanceData" | "ReceiveLevel" | "TransmissionPower" | "AdaptiveModulation" | "Temperature" | "SINR" | "CPD";
\ No newline at end of file
+export type PanelId = null | 'PerformanceData' | 'ReceiveLevel' | 'TransmissionPower' | 'AdaptiveModulation' | 'Temperature' | 'SINR' | 'CPD';
\ No newline at end of file
index 30f97fb..f71e09d 100644 (file)
@@ -15,7 +15,6 @@
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import { Moment } from "moment";
 
 //export { HitEntry, Result } from '../../../../framework/src/models';
 
@@ -50,5 +49,5 @@ export type PerformanceDataType = {
  */
 export const enum PmDataInterval {
   pmInterval15Min,
-  pmInterval24Hours
+  pmInterval24Hours,
 }
\ No newline at end of file
index 3b0cb76..5798d5c 100644 (file)
@@ -32,7 +32,7 @@ export type TemperatureDatabaseDataType = {
  * Internally used type to provide table and chart data
  */
 export type TemperatureDataType = {
-  performanceData: TemperatureDatabaseDataType
+  performanceData: TemperatureDatabaseDataType;
   radioSignalId: string;
   scannerId: string;
   timeStamp: string;
index f705e10..0e71c94 100644 (file)
@@ -19,7 +19,7 @@
 /**
  * Specifies possible sub views
  */
-export type SubTabType = "chart" | "table";
+export type SubTabType = 'chart' | 'table';
 
-export type currentViewType = "performanceData" | "receiveLevel" | "transmissionPower" | "adaptiveModulation" | "Temp" | "SINR" | "CPD";
+export type currentViewType = 'performanceData' | 'receiveLevel' | 'transmissionPower' | 'adaptiveModulation' | 'Temp' | 'SINR' | 'CPD';
 
index 99123f5..f52af97 100644 (file)
  */
 
 export interface TopologyNode {
-  "node-id": string;
+  'node-id': string;
 }
 
 export interface Topology {
-  "topology-id": string;
+  'topology-id': string;
   node: TopologyNode[];
 }
index a8aaca2..ef939fd 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
-import * as React from "react";
-import { faBook } from '@fortawesome/free-solid-svg-icons';
+import React from 'react';
+import { Redirect, Route, RouteComponentProps, Switch, withRouter } from 'react-router-dom';
 
+import { connect, Connect, IDispatcher } from '../../../framework/src/flux/connect';
 import applicationManager from '../../../framework/src/services/applicationManager';
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+import { IApplicationStoreState } from '../../../framework/src/store/applicationStore';
+import { ApplicationStore } from '../../../framework/src/store/applicationStore';
 
-import { withRouter, RouteComponentProps, Route, Switch, Redirect } from 'react-router-dom';
+import { updateMountIdActionCreator } from './actions/deviceListActions';
+import { ResetLtpsAction } from './actions/ltpAction';
+import { ReloadAction } from './actions/reloadAction';
+import { ResetAllSubViewsAction } from './actions/toggleActions';
 import performanceHistoryRootHandler from './handlers/performanceHistoryRootHandler';
 import { PmDataInterval } from './models/performanceDataType';
 import PerformanceHistoryApplication from './views/performanceHistoryApplication';
-import { ApplicationStore } from '../../../framework/src/store/applicationStore';
 
-import connect, { Connect, IDispatcher } from '../../../framework/src/flux/connect';
-import { IApplicationStoreState } from "../../../framework/src/store/applicationStore";
-import { updateMountIdActionCreator } from "./actions/deviceListActions";
-import { ResetAllSubViewsAction } from "./actions/toggleActions";
-import { ResetLtpsAction } from "./actions/ltpAction";
-import { ReloadAction } from "./actions/reloadAction";
+const appIcon = require('./assets/icons/performanceHistoryAppIcon.svg');  // select app icon
 
 let api: {
   readonly applicationStore: ApplicationStore | null;
   readonly applicationStoreInitialized: Promise<ApplicationStore>;
-}
+};
 
-const mapProps = (state: IApplicationStoreState) => ({
+const mapProps = () => ({
 });
 
 const mapDisp = (dispatcher: IDispatcher) => ({
   updateMountId: (mountId: string) => dispatcher.dispatch(updateMountIdActionCreator(mountId)),
   resetLtps: () => dispatcher.dispatch(new ResetLtpsAction()),
   resetSubViews: () => dispatcher.dispatch(new ResetAllSubViewsAction()),
-  setScheduleReload: (show: boolean) => dispatcher.dispatch(new ReloadAction(show))
+  setScheduleReload: (show: boolean) => dispatcher.dispatch(new ReloadAction(show)),
 });
 
 let currentMountId: string | null = null;
-let lastUrl: string = "/performanceHistory";
+let lastUrl: string = '/performanceHistory';
 const PerformanceHistoryApplicationRouteAdapter = connect(mapProps, mapDisp)((props: RouteComponentProps<{ mountId?: string }> & Connect<typeof mapProps, typeof mapDisp>) => {
-  let mountId: string = "";
+  let mountId: string = '';
+
+  const getMountId = (last_url: string) => {
+    let index = last_url.lastIndexOf('performanceHistory/');
+    if (index >= 0) {
+      mountId = last_url.substring(index + 19);
+    } else {
+      mountId = '';
+    }
 
-  // called when component finshed mounting
+    return mountId;
+  };
+
+  const scheduleReload = (current_mount_id: string) => {
+    props.updateMountId(current_mount_id);
+    props.resetLtps();
+    props.resetSubViews();
+    props.setScheduleReload(true);
+  };
+  
+  // called when component finished mounting
   React.useEffect(() => {
 
     lastUrl = props.location.pathname;
@@ -62,11 +81,11 @@ const PerformanceHistoryApplicationRouteAdapter = connect(mapProps, mapDisp)((pr
 
     if (currentMountId !== mountId) { // new element is loaded
       currentMountId = mountId;
-      schedueleReload(currentMountId);
+      scheduleReload(currentMountId);
     } else
-      if (currentMountId !== "") { // same element is loaded again
-        schedueleReload(currentMountId);
-      }
+    if (currentMountId !== '') { // same element is loaded again
+      scheduleReload(currentMountId);
+    }
   }, []);
 
   // called when component gets updated
@@ -77,52 +96,34 @@ const PerformanceHistoryApplicationRouteAdapter = connect(mapProps, mapDisp)((pr
 
     if (currentMountId !== mountId) {
       currentMountId = mountId;
-      schedueleReload(currentMountId);
+      scheduleReload(currentMountId);
     }
 
   });
 
-  const getMountId = (lastUrl: string) => {
-    let index = lastUrl.lastIndexOf("performanceHistory/");
-    if (index >= 0) {
-      mountId = lastUrl.substr(index + 19);
-    } else {
-      mountId = "";
-    }
-
-    return mountId;
-  }
-
-  const schedueleReload = (currentMountId: string) => {
-    props.updateMountId(currentMountId);
-    props.resetLtps();
-    props.resetSubViews();
-    props.setScheduleReload(true);
-  }
-
   return (
     <PerformanceHistoryApplication />
   );
 });
 
 const PerformanceHistoryRouterApp = withRouter((props: RouteComponentProps) => {
-  props.history.action = "POP";
+  props.history.action = 'POP';
   return (
     <Switch>
       <Route path={`${props.match.path}/:mountId`} component={PerformanceHistoryApplicationRouteAdapter} />
       <Route path={`${props.match.path}`} component={PerformanceHistoryApplicationRouteAdapter} />
       <Redirect to={`${props.match.path}`} />
     </Switch>
-  )
+  );
 });
 
 export function register() {
   api = applicationManager.registerApplication({
-    name: "performanceHistory",
-    icon: faBook,
+    name: 'performanceHistory',
+    icon: appIcon,
     rootComponent: PerformanceHistoryRouterApp,
     rootActionHandler: performanceHistoryRootHandler,
-    menuEntry: "Performance"
+    menuEntry: 'Performance',
   });
 }
 
index 70a8771..ef013f1 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import { requestRest } from '../../../../framework/src/services/restService';
 import { Result } from '../../../../framework/src/models/elasticSearch';
+import { requestRest } from '../../../../framework/src/services/restService';
 
 import { convertPropertyNames, replaceUpperCase } from '../../../../framework/src/utilities/yangHelper';
 import { LtpIds } from '../models/availableLtps';
 import { DeviceListType } from '../models/deviceListType';
-import { Topology, TopologyNode } from '../models/topologyNetconf';
 
 /** 
  * Represents a web api accessor service for Network elements actions.
@@ -34,26 +33,26 @@ class PerformanceService {
   public async getDistinctLtpsFromDatabase(networkElement: string, selectedTimePeriod: string): Promise<LtpIds[] | null> {
     let path;
     const query = {
-      "filter": [{
-        "property": "node-name",
-        "filtervalue": networkElement
+      'filter': [{
+        'property': 'node-name',
+        'filtervalue': networkElement,
       }],
-      "sortorder": [],
-      "pagination": {
-        "size": 20,
-        "page": 1
-      }
-    }
+      'sortorder': [],
+      'pagination': {
+        'size': 20,
+        'page': 1,
+      },
+    };
 
 
-    if (selectedTimePeriod === "15min") {
+    if (selectedTimePeriod === '15min') {
       path = '/rests/operations/data-provider:read-pmdata-15m-ltp-list';
     } else {
       path = '/rests/operations/data-provider:read-pmdata-24h-ltp-list';
     }
 
-    const result = await requestRest<Result<string>>(path, { method: "POST", body: JSON.stringify(convertPropertyNames({ input: query }, replaceUpperCase)) });
-    return result && result["data-provider:output"] && result["data-provider:output"].data && result["data-provider:output"].data.map(ne => ({ key: ne })) || null;
+    const result = await requestRest<Result<string>>(path, { method: 'POST', body: JSON.stringify(convertPropertyNames({ input: query }, replaceUpperCase)) });
+    return result && result['data-provider:output'] && result['data-provider:output'].data && result['data-provider:output'].data.map(ne => ({ key: ne })) || null;
   }
 
 
@@ -64,19 +63,19 @@ class PerformanceService {
   public async getDeviceListfromPerf15minHistory(): Promise<(DeviceListType)[] | null> {
     const path = '/rests/operations/data-provider:read-pmdata-15m-device-list';
     const query = {
-      "data-provider:input": {
-        "filter": [],
-        "sortorder": [],
-        "pagination": {
-          "size": 20,
-          "page": 1
-        }
-      }
+      'data-provider:input': {
+        'filter': [],
+        'sortorder': [],
+        'pagination': {
+          'size': 20,
+          'page': 1,
+        },
+      },
     };
 
-    const result = await requestRest<Result<string>>(path, { method: "POST", body: JSON.stringify(query) });
-    return result && result["data-provider:output"] && result["data-provider:output"].data && result["data-provider:output"].data.map(ne => ({
-      nodeId: ne
+    const result = await requestRest<Result<string>>(path, { method: 'POST', body: JSON.stringify(query) });
+    return result && result['data-provider:output'] && result['data-provider:output'].data && result['data-provider:output'].data.map(ne => ({
+      nodeId: ne,
     })) || null;
   }
 
@@ -86,19 +85,19 @@ class PerformanceService {
   public async getDeviceListfromPerf24hHistory(): Promise<(DeviceListType)[] | null> {
     const path = '/rests/operations/data-provider:read-pmdata-24h-device-list';
     const query = {
-      "data-provider:input": {
-        "filter": [],
-        "sortorder": [],
-        "pagination": {
-          "size": 20,
-          "page": 1
-        }
-      }
+      'data-provider:input': {
+        'filter': [],
+        'sortorder': [],
+        'pagination': {
+          'size': 20,
+          'page': 1,
+        },
+      },
     };
 
-    const result = await requestRest<Result<string>>(path, { method: "POST", body: JSON.stringify(query) });
-    return result && result["data-provider:output"] && result["data-provider:output"].data && result["data-provider:output"].data.map(ne => ({
-      nodeId: ne
+    const result = await requestRest<Result<string>>(path, { method: 'POST', body: JSON.stringify(query) });
+    return result && result['data-provider:output'] && result['data-provider:output'].data && result['data-provider:output'].data.map(ne => ({
+      nodeId: ne,
     })) || null;
   }
 }
index f58638e..38abb3e 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
-import { IDataSetsObject } from '../models/chartTypes';
+import React from 'react';
+import moment from 'moment';
 import { Line } from 'react-chartjs-2';
-import * as moment from 'moment';
-import { ITimeStamp } from 'models/chartTypes';
+
+import { IDataSetsObject } from '../models/chartTypes';
+import { ITimeStamp } from '../models/chartTypes';
 
 const style: React.CSSProperties = {
-  height: "80%"
-}
+  height: '80%',
+};
+
 export const lineChart = (chartPagedData: IDataSetsObject) => {
   return (
     <div style={style}>
@@ -44,32 +46,32 @@ export const lineChart = (chartPagedData: IDataSetsObject) => {
                 let offsetValue = new Date().getTimezoneOffset();
                 var utcDate = moment(date, 'YYYY-MM-DDTHH:mm:ss').utcOffset(offsetValue).utc(false);
                 return utcDate;
-              }
+              },
             },
             display: true,
             scaleLabel: {
               display: true,
-              labelString: 'Timestamp'
-            }
+              labelString: 'Timestamp',
+            },
           }],
           yAxes: [{
             ticks: {
-              beginAtZero: true
+              beginAtZero: true,
             },
             scaleLabel: {
               display: true,
-              labelString: 'Value'
-            }
-          }]
-        }
+              labelString: 'Value',
+            },
+          }],
+        },
       }} />
     </div>
   );
-}
+};
 
 export const sortDataByTimeStamp = <T extends ITimeStamp>(_rows: T[]): T[] => {
   return (_rows.sort((a, b) => {
-    const result = Date.parse(a["timeStamp"]) - Date.parse(b["timeStamp"]);
+    const result = Date.parse(a.timeStamp) - Date.parse(b.timeStamp);
     return isNaN(result) ? 0 : result;
   }));
-}
\ No newline at end of file
+};
\ No newline at end of file
index b5a3a3f..37fe962 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import { ColumnType, ColumnModel } from '../../../../framework/src/components/material-table';
+import { ColumnModel, ColumnType } from '../../../../framework/src/components/material-table';
 
 import { PmDataInterval } from '../models/performanceDataType';
 import { getPmDataInterval } from '../pluginPerformance';
 
 export const addColumnLabels = <T>(name: string, title: string, disableFilter = true, disableSorting = true): ColumnModel<T> => {
   return { property: name as keyof T, title: title, type: ColumnType.text, disableFilter: disableFilter, disableSorting: disableSorting };
-}
+};
 
 export function getFilter(): string {
   switch (getPmDataInterval()) {
-      case PmDataInterval.pmInterval15Min:
-          return "pmdata-15m";
-      case PmDataInterval.pmInterval24Hours:
-          return "pmdata-24h";
-      default:
-          throw new Error("Unknown time intervall");
+    case PmDataInterval.pmInterval15Min:
+      return 'pmdata-15m';
+    case PmDataInterval.pmInterval24Hours:
+      return 'pmdata-24h';
+    default:
+      throw new Error('Unknown time intervall');
   }
 }
\ No newline at end of file
index b33b442..a4b9686 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
+import React from 'react';
+import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect';
 
 import { Theme } from '@mui/material/styles';
 import { WithStyles } from '@mui/styles';
 import createStyles from '@mui/styles/createStyles';
 import withStyles from '@mui/styles/withStyles';
-import FormControl from '@mui/material/FormControl';
-import MenuItem from '@mui/material/MenuItem';
-import Select from '@mui/material/Select';
 
-import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect';
-import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
-import { Panel, Loader } from '../../../../framework/src/components/material-ui';
+
 import { NavigateToApplication } from '../../../../framework/src/actions/navigationActions';
 import { Dispatch } from '../../../../framework/src/flux/store';
+import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
 
-import { PanelId } from '../models/panelId';
-import { PmDataInterval } from '../models/performanceDataType';
-import PerformanceData from '../components/performanceData';
-import ReceiveLevel from '../components/receiveLevel';
-import TransmissionPower from '../components/transmissionPower';
-import AdaptiveModulation from '../components/adaptiveModulation';
-import Temperature from '../components/temperature';
-import SignalToInterference from '../components/signalToInterference';
-import CrossPolarDiscrimination from '../components/crossPolarDiscrimination';
 import { loadAllDeviceListAsync } from '../actions/deviceListActions';
-import { TimeChangeAction } from '../actions/timeChangeAction';
 import { loadDistinctLtpsbyNetworkElementAsync, ResetLtpsAction } from '../actions/ltpAction';
 import { SetPanelAction } from '../actions/panelChangeActions';
-import { createPerformanceDataPreActions, performanceDataReloadAction, createPerformanceDataActions } from '../handlers/performanceDataHandler';
-import { createReceiveLevelPreActions, receiveLevelReloadAction, createReceiveLevelActions } from '../handlers/receiveLevelHandler';
-import { createTransmissionPowerPreActions, transmissionPowerReloadAction, createTransmissionPowerActions } from '../handlers/transmissionPowerHandler';
-import { createAdaptiveModulationPreActions, adaptiveModulationReloadAction, createAdaptiveModulationActions } from '../handlers/adaptiveModulationHandler';
-import { createTemperaturePreActions, temperatureReloadAction, createTemperatureActions } from '../handlers/temperatureHandler';
-import { createSignalToInterferencePreActions, signalToInterferenceReloadAction, createSignalToInterferenceActions } from '../handlers/signalToInterferenceHandler';
-import { createCrossPolarDiscriminationPreActions, crossPolarDiscriminationReloadAction, createCrossPolarDiscriminationActions } from '../handlers/crossPolarDiscriminationHandler';
+import { TimeChangeAction } from '../actions/timeChangeAction';
+import AdaptiveModulation from '../components/adaptiveModulation';
+import CrossPolarDiscrimination from '../components/crossPolarDiscrimination';
+import PerformanceData from '../components/performanceData';
+import ReceiveLevel from '../components/receiveLevel';
+import SignalToInterference from '../components/signalToInterference';
+import Temperature from '../components/temperature';
+import TransmissionPower from '../components/transmissionPower';
+import { adaptiveModulationReloadAction, createAdaptiveModulationActions, createAdaptiveModulationPreActions } from '../handlers/adaptiveModulationHandler';
+import { createCrossPolarDiscriminationActions, createCrossPolarDiscriminationPreActions, crossPolarDiscriminationReloadAction } from '../handlers/crossPolarDiscriminationHandler';
+import { createPerformanceDataActions, createPerformanceDataPreActions, performanceDataReloadAction } from '../handlers/performanceDataHandler';
+import { createReceiveLevelActions, createReceiveLevelPreActions, receiveLevelReloadAction } from '../handlers/receiveLevelHandler';
+import { createSignalToInterferenceActions, createSignalToInterferencePreActions, signalToInterferenceReloadAction } from '../handlers/signalToInterferenceHandler';
+import { createTemperatureActions, createTemperaturePreActions, temperatureReloadAction } from '../handlers/temperatureHandler';
+import { createTransmissionPowerActions, createTransmissionPowerPreActions, transmissionPowerReloadAction } from '../handlers/transmissionPowerHandler';
+import { PanelId } from '../models/panelId';
+import { PmDataInterval } from '../models/performanceDataType';
 
+import { AppBar, SelectChangeEvent, Tab, Tabs } from '@mui/material';
 import { MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table';
-import { AppBar, Tabs, Tab, SelectChangeEvent } from '@mui/material';
-import LtpSelection from '../components/ltpSelection';
-import { ResetAllSubViewsAction } from '../actions/toggleActions';
 import { ReloadAction } from '../actions/reloadAction';
+import { ResetAllSubViewsAction } from '../actions/toggleActions';
+import LtpSelection from '../components/ltpSelection';
 
 const PerformanceHistoryComponentStyles = (theme: Theme) => createStyles({
   root: {
-    display: "flex",
-    flexWrap: "wrap",
+    display: 'flex',
+    flexWrap: 'wrap',
   },
   margin: {
     margin: theme.spacing(1),
-  }
+  },
 });
 
 const mapProps = (state: IApplicationStoreState) => ({
@@ -75,7 +72,7 @@ const mapProps = (state: IApplicationStoreState) => ({
   networkElements: state.performanceHistory.networkElements.deviceList,
   initialLoaded: state.performanceHistory.ltps.loadedOnce,
   error: state.performanceHistory.ltps.error,
-  shouldReload: state.performanceHistory.isReloadSchedueled
+  shouldReload: state.performanceHistory.isReloadSchedueled,
 });
 
 const mapDispatcher = (dispatcher: IDispatcher) => ({
@@ -103,112 +100,117 @@ const mapDispatcher = (dispatcher: IDispatcher) => ({
   getAllDevicesPMdata: async () => {
     await dispatcher.dispatch(loadAllDeviceListAsync);
   },
-  getDistinctLtpsIds: (selectedNetworkElement: string, selectedTimePeriod: string, selectedLtp: string, selectFirstLtp?: Function, resetLTP?: Function) => dispatcher.dispatch(loadDistinctLtpsbyNetworkElementAsync(selectedNetworkElement, selectedTimePeriod, selectedLtp, selectFirstLtp, resetLTP)),
+  getDistinctLtpsIds: (
+    selectedNetworkElement: string, 
+    selectedTimePeriod: string, 
+    selectedLtp: string, 
+    selectFirstLtp?: Function, 
+    resetLTP?: Function,
+  ) => dispatcher.dispatch(loadDistinctLtpsbyNetworkElementAsync(selectedNetworkElement, selectedTimePeriod, selectedLtp, selectFirstLtp, resetLTP)),
   setCurrentPanel: (panelId: PanelId) => dispatcher.dispatch(new SetPanelAction(panelId)),
   timeIntervalChange: (time: PmDataInterval) => dispatcher.dispatch(new TimeChangeAction(time)),
   changeNode: (nodeId: string) => dispatcher.dispatch((dispatch: Dispatch) => {
-    dispatch(new NavigateToApplication("performanceHistory", nodeId));
+    dispatch(new NavigateToApplication('performanceHistory', nodeId));
   }),
   resetLtps: () => dispatcher.dispatch((dispatch: Dispatch) => { dispatch(new ResetLtpsAction()); }),
   resetSubViews: () => dispatcher.dispatch(new ResetAllSubViewsAction()),
-  setShouldReload: (show: boolean) => dispatcher.dispatch(new ReloadAction(show))
+  setShouldReload: (show: boolean) => dispatcher.dispatch(new ReloadAction(show)),
 });
 
 export type NetworkElementType = {
-  nodeId: string,
-}
+  nodeId: string;
+};
+
 const NetworkElementTable = MaterialTable as MaterialTableCtorType<NetworkElementType>;
 
 type PerformanceHistoryComponentProps = Connect<typeof mapProps, typeof mapDispatcher> & WithStyles<typeof PerformanceHistoryComponentStyles>;
 
 type PerformanceHistoryComponentState = {
-  selectedNetworkElement: string,
-  selectedTimePeriod: string,
-  selectedLtp: string,
-  showNetworkElementsTable: boolean,
-  showLtps: boolean,
-  showPanels: boolean,
+  selectedNetworkElement: string;
+  selectedTimePeriod: string;
+  selectedLtp: string;
+  showNetworkElementsTable: boolean;
+  showLtps: boolean;
+  showPanels: boolean;
   preFilter:
   {
-    "node-name": string,
-    "uuid-interface": string
-  } | {}
+    'node-name': string;
+    'uuid-interface': string;
+  } | {};
 };
 
 /**
 * Represents the component for Performance history application.
 */
-class PerformanceHistoryComponent extends React.Component<PerformanceHistoryComponentProps, PerformanceHistoryComponentState>{
+class PerformanceHistoryComponent extends React.Component<PerformanceHistoryComponentProps, PerformanceHistoryComponentState> {
   /**
   * Initialises this instance
   */
   constructor(props: PerformanceHistoryComponentProps) {
     super(props);
     this.state = {
-      selectedNetworkElement: props.nodeId !== "" ? props.nodeId : "-1",
-      selectedTimePeriod: "15min",
-      selectedLtp: "-1",
+      selectedNetworkElement: props.nodeId !== '' ? props.nodeId : '-1',
+      selectedTimePeriod: '15min',
+      selectedLtp: '-1',
       showNetworkElementsTable: true,
       showLtps: false,
       showPanels: false,
-      preFilter: {}
+      preFilter: {},
     };
   }
 
   onChangeTabs = (event: React.SyntheticEvent, newValue: PanelId) => {
     const nextActivePanel = newValue;
     this.changeTabs(nextActivePanel);
-  }
+  };
 
   changeTabs = (nextActivePanel: PanelId) => {
     this.props.setCurrentPanel(nextActivePanel);
     const preFilter = this.state.preFilter;
     switch (nextActivePanel) {
-      case "PerformanceData":
+      case 'PerformanceData':
         if (this.props.performanceData.preFilter !== {} && this.props.performanceData.preFilter === preFilter) {
           this.props.reloadPerformanceData();
         } else {
           this.props.performanceDataPreActions.onPreFilterChanged(preFilter);
         }
         break;
-      case "ReceiveLevel":
+      case 'ReceiveLevel':
         if (this.props.receiveLevel.preFilter !== {} && this.props.receiveLevel.preFilter === preFilter) {
           this.props.reloadReceiveLevel();
-        }
-        else {
+        } else {
           this.props.receiveLevelPreActions.onPreFilterChanged(preFilter);
         }
         break;
-      case "TransmissionPower":
+      case 'TransmissionPower':
         if (this.props.transmissionPower.preFilter !== {} && this.props.transmissionPower.preFilter === preFilter) {
           this.props.reloadTransmissionPower();
-        }
-        else {
+        } else {
           this.props.transmissionPowerPreActions.onPreFilterChanged(preFilter);
         }
         break;
-      case "AdaptiveModulation":
+      case 'AdaptiveModulation':
         if (this.props.adaptiveModulation.preFilter !== {} && this.props.adaptiveModulation.preFilter === preFilter) {
           this.props.reloadAdaptiveModulation();
         } else {
           this.props.adaptiveModulationPreActions.onPreFilterChanged(preFilter);
         }
         break;
-      case "Temperature":
+      case 'Temperature':
         if (this.props.temperature.preFilter !== {} && this.props.temperature.preFilter === preFilter) {
           this.props.reloadTemperature();
         } else {
           this.props.temperaturePreActions.onPreFilterChanged(preFilter);
         }
         break;
-      case "SINR":
+      case 'SINR':
         if (this.props.signalToInterference.preFilter !== {} && this.props.signalToInterference.preFilter === preFilter) {
           this.props.reloadSignalToInterference();
         } else {
           this.props.signalToInterferencePreActions.onPreFilterChanged(preFilter);
         }
         break;
-      case "CPD":
+      case 'CPD':
         if (this.props.crossPolarDiscrimination.preFilter !== {} && this.props.crossPolarDiscrimination.preFilter === preFilter) {
           this.props.reloadCrossPolarDiscrimination();
         } else {
@@ -219,21 +221,20 @@ class PerformanceHistoryComponent extends React.Component<PerformanceHistoryComp
         // do nothing if all panels are closed
         break;
     }
-  }
+  };
 
   render(): JSX.Element {
     const { activePanel, nodeId } = this.props;
-    if (nodeId === "") {
+    if (nodeId === '') {
       return (
         <>
-          <NetworkElementTable tableId="performance-data-element-selection-table" defaultSortColumn={"nodeId"} defaultSortOrder="asc" stickyHeader title={"Please select the network element!"} idProperty={"nodeId"} rows={this.props.networkElements} asynchronus
-            onHandleClick={(event, rowData) => { this.handleNetworkElementSelect(rowData.nodeId) }} columns={
-              [{ property: "nodeId", title: "Node Name" }]
+          <NetworkElementTable tableId="performance-data-element-selection-table" defaultSortColumn={'nodeId'} defaultSortOrder="asc" stickyHeader title={'Please select the network element!'} idProperty={'nodeId'} rows={this.props.networkElements} asynchronus
+            onHandleClick={(event, rowData) => { this.handleNetworkElementSelect(rowData.nodeId); }} columns={
+              [{ property: 'nodeId', title: 'Node Name' }]
             } />
         </>
-      )
-    }
-    else {
+      );
+    } else {
       this.handleURLChange(nodeId);
       return (
         <>
@@ -259,42 +260,42 @@ class PerformanceHistoryComponent extends React.Component<PerformanceHistoryComp
                 </Tabs>
               </AppBar>
               {
-                activePanel === "PerformanceData" &&
+                activePanel === 'PerformanceData' &&
                 <PerformanceData selectedTimePeriod={this.state.selectedTimePeriod} />
               }
 
               {
-                activePanel === "ReceiveLevel" &&
+                activePanel === 'ReceiveLevel' &&
                 <ReceiveLevel selectedTimePeriod={this.state.selectedTimePeriod} />
               }
 
               {
-                activePanel === "TransmissionPower" &&
+                activePanel === 'TransmissionPower' &&
                 <TransmissionPower selectedTimePeriod={this.state.selectedTimePeriod} />
               }
 
               {
-                activePanel === "AdaptiveModulation" &&
+                activePanel === 'AdaptiveModulation' &&
                 <AdaptiveModulation selectedTimePeriod={this.state.selectedTimePeriod} />
               }
               {
-                activePanel === "Temperature" &&
+                activePanel === 'Temperature' &&
                 <Temperature selectedTimePeriod={this.state.selectedTimePeriod} />
               }
 
               {
-                activePanel === "SINR" &&
+                activePanel === 'SINR' &&
                 <SignalToInterference selectedTimePeriod={this.state.selectedTimePeriod} />
               }
 
               {
-                activePanel === "CPD" &&
+                activePanel === 'CPD' &&
                 <CrossPolarDiscrimination selectedTimePeriod={this.state.selectedTimePeriod} />
               }
             </>
           }
         </>
-      )
+      );
     }
   }
 
@@ -310,19 +311,19 @@ class PerformanceHistoryComponent extends React.Component<PerformanceHistoryComp
   private selectFirstLtp = (firstLtp: string) => {
     this.setState({
       showPanels: true,
-      selectedLtp: firstLtp
+      selectedLtp: firstLtp,
     });
     this.preFilterChangeAndReload(this.state.selectedNetworkElement, this.state.selectedTimePeriod, firstLtp);
-    this.changeTabs("PerformanceData");
-  }
+    this.changeTabs('PerformanceData');
+  };
 
   /**
   * A function which reloads the visible table, if available, based on prefilters defined by network element and ltp on selected time period.
   */
   private preFilterChangeAndReload = (networkElement: string, timePeriod: string, ltp: string) => {
     const newPreFilter = {
-      "node-name": networkElement,
-      "uuid-interface": ltp
+      'node-name': networkElement,
+      'uuid-interface': ltp,
     };
 
     const activePanel = this.props.activePanel;
@@ -331,25 +332,25 @@ class PerformanceHistoryComponent extends React.Component<PerformanceHistoryComp
       // set prefilter and reload data if panel is open
 
       switch (activePanel) {
-        case "PerformanceData":
+        case 'PerformanceData':
           this.props.performanceDataPreActions.onPreFilterChanged(newPreFilter);
           break;
-        case "ReceiveLevel":
+        case 'ReceiveLevel':
           this.props.receiveLevelPreActions.onPreFilterChanged(newPreFilter);
           break;
-        case "TransmissionPower":
+        case 'TransmissionPower':
           this.props.transmissionPowerPreActions.onPreFilterChanged(newPreFilter);
           break;
-        case "AdaptiveModulation":
+        case 'AdaptiveModulation':
           this.props.adaptiveModulationPreActions.onPreFilterChanged(newPreFilter);
           break;
-        case "Temperature":
+        case 'Temperature':
           this.props.temperaturePreActions.onPreFilterChanged(newPreFilter);
           break;
-        case "SINR":
+        case 'SINR':
           this.props.signalToInterferencePreActions.onPreFilterChanged(newPreFilter);
           break;
-        case "CPD":
+        case 'CPD':
           this.props.crossPolarDiscriminationPreActions.onPreFilterChanged(newPreFilter);
           break;
         default:
@@ -359,9 +360,9 @@ class PerformanceHistoryComponent extends React.Component<PerformanceHistoryComp
     }
 
     // set prefilter
-    this.setState({ preFilter: newPreFilter })
+    this.setState({ preFilter: newPreFilter });
 
-  }
+  };
 
   /**
    * Function which handles network element changes.
@@ -373,15 +374,15 @@ class PerformanceHistoryComponent extends React.Component<PerformanceHistoryComp
       selectedNetworkElement: selectedNetworkElement,
       showNetworkElementsTable: false,
       showPanels: false,
-      selectedLtp: "-1"
+      selectedLtp: '-1',
     });
 
     this.props.resetSubViews();
     this.props.resetLtps();
     this.setState({ preFilter: {} });
     this.props.changeNode(selectedNetworkElement);
-    this.props.getDistinctLtpsIds(selectedNetworkElement, this.state.selectedTimePeriod, "-1", this.selectFirstLtp);
-  }
+    this.props.getDistinctLtpsIds(selectedNetworkElement, this.state.selectedTimePeriod, '-1', this.selectFirstLtp);
+  };
 
   private handleURLChange = (selectedNetworkElement: string) => {
 
@@ -391,12 +392,12 @@ class PerformanceHistoryComponent extends React.Component<PerformanceHistoryComp
         showLtps: true,
         selectedNetworkElement: selectedNetworkElement,
         showPanels: false,
-        selectedLtp: "-1"
+        selectedLtp: '-1',
       });
-      this.props.getDistinctLtpsIds(selectedNetworkElement, this.state.selectedTimePeriod, "-1", this.selectFirstLtp);
+      this.props.getDistinctLtpsIds(selectedNetworkElement, this.state.selectedTimePeriod, '-1', this.selectFirstLtp);
       this.props.setShouldReload(false);
     }
-  }
+  };
 
   /**
     * Function which resets the ltps to "--select--" in the state if the passed parameter @ltpNotSelected is true.
@@ -405,18 +406,18 @@ class PerformanceHistoryComponent extends React.Component<PerformanceHistoryComp
   private resetLtpDropdown = (ltpNotSelected: boolean) => {
     if (ltpNotSelected) {
       this.setState({
-        selectedLtp: "-1",
-        showPanels: false
+        selectedLtp: '-1',
+        showPanels: false,
       });
     }
-  }
+  };
 
   /**
   * Function which handles the time period changes.
   */
   private handleTimePeriodChange = (event: SelectChangeEvent<HTMLSelectElement>) => {
 
-    const selectedTimeInterval = event.target.value === "15min"
+    const selectedTimeInterval = event.target.value === '15min'
       ? PmDataInterval.pmInterval15Min
       : PmDataInterval.pmInterval24Hours;
 
@@ -427,28 +428,28 @@ class PerformanceHistoryComponent extends React.Component<PerformanceHistoryComp
     this.props.timeIntervalChange(selectedTimeInterval);
     this.props.getDistinctLtpsIds(this.state.selectedNetworkElement, event.target.value as string, this.state.selectedLtp, undefined, this.resetLtpDropdown);
     this.preFilterChangeAndReload(this.state.selectedNetworkElement, event.target.value as string, this.state.selectedLtp);
-  }
+  };
 
   /**
   * Function which handles the ltp changes.
   */
   private handleLtpChange = (event:SelectChangeEvent<HTMLSelectElement> ) => {
 
-    if (event.target.value === "-1") {
+    if (event.target.value === '-1') {
       this.setState({
         showPanels: false,
-        selectedLtp: event.target.value
+        selectedLtp: event.target.value,
       });
 
     } else if (event.target.value !== this.state.selectedLtp) {
       this.setState({
         showPanels: true,
-        selectedLtp: event.target.value as string
+        selectedLtp: event.target.value as string,
       });
       this.preFilterChangeAndReload(this.state.selectedNetworkElement, this.state.selectedTimePeriod, event.target.value as string);
 
     }
-  }
+  };
 }
 
 const PerformanceHistoryApplication = withStyles(PerformanceHistoryComponentStyles)(connect(mapProps, mapDispatcher)(PerformanceHistoryComponent));
index a66b5d8..ca65092 100644 (file)
@@ -4,7 +4,7 @@
     "outDir": "./dist",
     "sourceMap": true,
     "forceConsistentCasingInFileNames": true,
-    "allowSyntheticDefaultImports": false,
+    "allowSyntheticDefaultImports": true,
     "allowUnreachableCode": false,
     "allowUnusedLabels": false,
     "noFallthroughCasesInSwitch": true,
index 8f2192f..2f25d0d 100644 (file)
@@ -57,6 +57,16 @@ module.exports = (env) => {
         use: [{
           loader: "babel-loader"
         }]
+      },{
+        //don't minify images
+        test: /\.(png|gif|jpg|svg)$/,
+        use: [{
+          loader: 'url-loader',
+          options: {
+            limit: 10,
+            name: './images/[name].[ext]'
+          }
+        }]
       }]
     },
 
diff --git a/sdnr/wt/odlux/eslintrc.json b/sdnr/wt/odlux/eslintrc.json
new file mode 100644 (file)
index 0000000..f98e65b
--- /dev/null
@@ -0,0 +1,54 @@
+{
+  "root": true,
+  "parser": "@typescript-eslint/parser",
+  "plugins": [
+    "import",
+    "@typescript-eslint",
+    "react",
+    "react-hooks"
+  ],
+  "extends": [
+    "airbnb-typescript"
+  ],
+  "parserOptions": {
+    "project": [
+      "./tsconfig.json"
+    ],
+    "sourceType": "module"
+  },
+  "settings": {
+    "react": {
+      "version": "detect"
+    }
+  },
+  "rules": { 
+    "no-console": "off",
+    "no-debugger": "off",
+    "import/no-cycle": "off",
+    "quotes": [ "error", "single" ],
+    "import/prefer-default-export": "off",
+    "lines-between-class-members": "off",
+    "no-nested-ternary": "off",
+    "no-unused-vars": "off",
+    "object-curly-newline": "off", //["error", { "multiline": true, "minProperties": 8, "consistent": true }], 
+    "max-len": [ 2, 280, 2, { "ignoreUrls": true } ],
+    "@typescript-eslint/lines-between-class-members": [ "error", "always", { "exceptAfterOverload": true } ],
+    "@typescript-eslint/quotes": [ "error", "single" , { "avoidEscape": true } ],
+    "@typescript-eslint/no-unused-vars": [ "error", { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_", "caughtErrorsIgnorePattern": "^_" } ],
+    "@typescript-eslint/naming-convention": [ "error", 
+        { "format": [ "camelCase", "PascalCase", "UPPER_CASE", "snake_case" ], "leadingUnderscore": "allow", "selector": "default", "filter": { "regex": "(^&)|(^\\w+(-\\w+)+)", "match": false } } ],
+    "no-underscore-dangle": [ "error", { "allowAfterThis": true } ],
+    "no-param-reassign": [ "error", { "props": false } ],
+    "react/prop-types": [ "error", { "skipUndeclared": true } ],
+    "@typescript-eslint/member-delimiter-style": ["error"]
+  },
+  "overrides": [
+    { 
+      "files": "**/handlers/*Handler.ts",
+      "rules": {
+        "no-param-reassign": "off",
+        "@typescript-eslint/default-param-last": "off"
+      }
+    }
+  ]
+}
\ No newline at end of file
index 414f498..3dd0f1a 100644 (file)
@@ -19,6 +19,7 @@
   ~ ============LICENSE_END=======================================================
   ~
   -->
+
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
@@ -45,7 +46,7 @@
     <properties>
         <buildtime>${maven.build.timestamp}</buildtime>
         <distversion>ONAP Frankfurt (Neon, mdsal ${odl.mdsal.version})</distversion>
-        <buildno>142.63ceae1(22/01/31)</buildno>
+        <buildno>178.3bd8a2a9(23/03/16)</buildno>
         <odlux.version>ONAP SDN-R | ONF Wireless for ${distversion} - Build: ${buildtime} ${buildno} ${project.version}</odlux.version>
     </properties>
 
                         <!-- optional: default phase is "generate-resources" -->
                         <phase>initialize</phase>
                         <configuration>
-                            <nodeVersion>v12.13.0</nodeVersion>
+                            <nodeVersion>v12.22.0</nodeVersion>
                             <yarnVersion>v1.22.10</yarnVersion>
                         </configuration>
                     </execution>
index 1c18ddc..092b318 100644 (file)
@@ -19,7 +19,7 @@
 import { Dispatch } from "../flux/store";
 import { Action } from "../flux/action";
 import { GeneralSettings, Settings, TableSettings, TableSettingsColumn } from "../models/settings";
-import { getSettings, putSettings } from "../services/settingsService";
+import { getUserdata, saveUserdata } from "../services/userdataService";
 import { startWebsocketSession, suspendWebsocketSession } from "../services/notificationService";
 import { IApplicationStoreState } from "../store/applicationStore";
 
@@ -67,7 +67,7 @@ export const setGeneralSettingsAction = (value: boolean) => (dispatcher: Dispatc
 export const updateGeneralSettingsAction = (activateNotifications: boolean) => async (dispatcher: Dispatch) => {
 
     const value: GeneralSettings = { general: { areNotificationsEnabled: activateNotifications } };
-    const result = await putSettings("/general", JSON.stringify(value.general));
+    const result = await saveUserdata("/general", JSON.stringify(value.general));
     dispatcher(setGeneralSettingsAction(activateNotifications));
 
 }
@@ -86,14 +86,14 @@ export const updateTableSettings = (tableName: string, columns: TableSettingsCol
     // would only save latest entry
     //const json = JSON.stringify({ [tableName]: { columns: columns } });
 
-    const result = await putSettings("/tables", json);
+    const result = await saveUserdata("/tables", json);
 
     dispatcher(new SetTableSettings(tableName, columns));
 }
 
 export const getGeneralSettingsAction = () => async (dispatcher: Dispatch) => {
 
-    const result = await getSettings<GeneralSettings>();
+    const result = await getUserdata<GeneralSettings>();
 
     if (result && result.general) {
         dispatcher(new SetGeneralSettingsAction(result.general.areNotificationsEnabled!))
index 9b03a21..bbe1f9e 100644 (file)
@@ -32,14 +32,15 @@ import { applicationStoreCreator } from './store/applicationStore';
 import { ApplicationStoreProvider } from './flux/connect';
 
 import { startHistoryListener } from './middleware/navigation';
-import { startRestService } from './services/restService';
+import { startSoreService } from './services/storeService';
 
 import { startUserSessionService } from './services/userSessionService';
 import { startNotificationService } from './services/notificationService';
 
+import { startBroadcastChannel } from './services/broadcastService';
+
 import theme from './design/default';
 import '!style-loader!css-loader!./app.css';
-import { startBroadcastChannel } from './services/broadcastService';
 
 declare module '@mui/material/styles' {
 
@@ -98,7 +99,7 @@ export const runApplication = () => {
   };
   
 
-  startRestService(applicationStore);
+  startSoreService(applicationStore);
   startHistoryListener(applicationStore);
   startNotificationService(applicationStore);
 
diff --git a/sdnr/wt/odlux/framework/src/assets/icons/About.svg b/sdnr/wt/odlux/framework/src/assets/icons/About.svg
new file mode 100644 (file)
index 0000000..156e36e
--- /dev/null
@@ -0,0 +1,18 @@
+<!-- highstreet technologies GmbH colour scheme \r
+       Grey    #565656\r
+       LBlue #36A9E1\r
+       DBlue #246DA2\r
+       Green #003F2C / #006C4B\r
+       Yellw #C8D400\r
+       Red     #D81036\r
+-->\r
+\r
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">\r
+\r
+<path fill="#36A9E1" d="M 13 13 V 17.5 A 1 1 0 0 1 11 17.5 V 13 A 1 1 0 0 1 13 13 Z "/>\r
+\r
+<path fill="#36A9E1" d="M 12 11 A 1 1 0 1 0 11 10 A 1 1 0 0 0 12 11 Z "/>\r
+\r
+<path fill="#565656" d="M 1.125 20.485 A 1 1 0 0 1 1.152 19.47 L 11.152 3.47 A 1.039 1.039 0 0 1 12.852 3.47 L 22.852 19.47 A 1 1 0 0 1 22 21 H 2 A 1 1 0 0 1 1.125 20.485 Z M 3.8 19 H 20.2 L 12 5.887 Z"/>\r
+\r
+</svg>
\ No newline at end of file
diff --git a/sdnr/wt/odlux/framework/src/assets/icons/Home.svg b/sdnr/wt/odlux/framework/src/assets/icons/Home.svg
new file mode 100644 (file)
index 0000000..0836714
--- /dev/null
@@ -0,0 +1,28 @@
+<!-- highstreet technologies GmbH colour scheme \r
+       Grey    #565656\r
+       LBlue #36A9E1\r
+       DBlue #246DA2\r
+       Green #003F2C / #006C4B\r
+       Yellw #C8D400\r
+       Red     #D81036\r
+-->\r
+\r
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 460.298 460.297" style="enable-background:new 0 0 460.298 460.297;"\r
+        xml:space="preserve">\r
+<g>\r
+       <g>\r
+               <path fill="#565656" d="M230.149,120.939L65.986,256.274c0,0.191-0.048,0.472-0.144,0.855c-0.094,0.38-0.144,0.656-0.144,0.852v137.041\r
+                       c0,4.948,1.809,9.236,5.426,12.847c3.616,3.613,7.898,5.431,12.847,5.431h109.63V303.664h73.097v109.64h109.629\r
+                       c4.948,0,9.236-1.814,12.847-5.435c3.617-3.607,5.432-7.898,5.432-12.847V257.981c0-0.76-0.104-1.334-0.288-1.707L230.149,120.939\r
+                       z"/>\r
+               <path fill="#565656" d="M457.122,225.438L394.6,173.476V56.989c0-2.663-0.856-4.853-2.574-6.567c-1.704-1.712-3.894-2.568-6.563-2.568h-54.816\r
+                       c-2.666,0-4.855,0.856-6.57,2.568c-1.711,1.714-2.566,3.905-2.566,6.567v55.673l-69.662-58.245\r
+                       c-6.084-4.949-13.318-7.423-21.694-7.423c-8.375,0-15.608,2.474-21.698,7.423L3.172,225.438c-1.903,1.52-2.946,3.566-3.14,6.136\r
+                       c-0.193,2.568,0.472,4.811,1.997,6.713l17.701,21.128c1.525,1.712,3.521,2.759,5.996,3.142c2.285,0.192,4.57-0.476,6.855-1.998\r
+                       L230.149,95.817l197.57,164.741c1.526,1.328,3.521,1.991,5.996,1.991h0.858c2.471-0.376,4.463-1.43,5.996-3.138l17.703-21.125\r
+                       c1.522-1.906,2.189-4.145,1.991-6.716C460.068,229.007,459.021,226.961,457.122,225.438z"/>\r
+\r
+<path fill="#246DA2" d="M 457.122 225.438 L 251.849 54.417 L 251.849 54.417 C 245.765 49.468 238.531 46.994 230.155 46.994 C 221.78 46.994 214.547 49.468 208.457 54.417 L 3.172 225.438 C 1.269 226.958 0.226 229.004 0.032 231.574 C -0.161 234.142 0.504 236.385 2.029 238.287 L 19.73 259.415 C 21.255 261.127 23.251 262.174 25.726 262.557 C 28.011 262.749 30.296 262.081 32.581 260.559 L 230.149 95.817 L 427.719 260.558 C 429.245 261.886 431.24 262.549 433.715 262.549 H 434.573 C 437.044 262.173 439.036 261.119 440.569 259.411 L 458.272 238.286 C 459.794 236.38 460.461 234.141 460.263 231.57 C 460.068 229.007 459.021 226.961 457.122 225.438 Z"/>\r
+       </g>\r
+</g>\r
+</svg>\r
diff --git a/sdnr/wt/odlux/framework/src/assets/icons/Menu.svg b/sdnr/wt/odlux/framework/src/assets/icons/Menu.svg
new file mode 100644 (file)
index 0000000..ea03128
--- /dev/null
@@ -0,0 +1,18 @@
+<!-- highstreet technologies GmbH colour scheme \r
+       Grey    #565656\r
+       LBlue #36A9E1\r
+       DBlue #246DA2\r
+       Green #003F2C / #006C4B\r
+       Yellw #C8D400\r
+       Red     #D81036\r
+-->\r
+\r
+<svg viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">\r
+\r
+<path fill="#36A9E1" d="M4,10h24c1.104,0,2-0.896,2-2s-0.896-2-2-2H4C2.896,6,2,6.896,2,8S2.896,10,4,10z"/>\r
+\r
+<path fill="#36A9E1" d="M28,14H4c-1.104,0-2,0.896-2,2  s0.896,2,2,2h24c1.104,0,2-0.896,2-2S29.104,14,28,14z"/>\r
+\r
+<path fill="#36A9E1" d="M28,22H4c-1.104,0-2,0.896-2,2s0.896,2,2,2h24c1.104,0,2-0.896,2-2  S29.104,22,28,22z"/>\r
+\r
+</svg>
\ No newline at end of file
diff --git a/sdnr/wt/odlux/framework/src/assets/icons/Tools.svg b/sdnr/wt/odlux/framework/src/assets/icons/Tools.svg
new file mode 100644 (file)
index 0000000..1595cdc
--- /dev/null
@@ -0,0 +1,35 @@
+<!-- highstreet technologies GmbH colour scheme 
+       Grey    #565656
+       LBlue #36A9E1
+       DBlue #246DA2
+       Green #003F2C / #006C4B
+       Yellw #C8D400
+       Red     #D81036
+-->
+
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 265 265">
+
+<g transform="translate(0.000000,265.000000) scale(0.100000,-0.100000)">
+
+<path fill="#565656" d="M601 2515 c-35 -8 -66 -17 -69 -20 -3 -3 76 -87 175 -188 243 -244
+243 -246 59 -425 -106 -103 -137 -120 -194 -107 -19 4 -88 66 -207 185 -99 99
+-183 180 -186 180 -17 0 -34 -104 -33 -200 0 -82 6 -120 23 -170 61 -174 191
+-306 362 -367 62 -22 93 -27 187 -28 l113 -2 602 -600 c673 -671 633 -638 777
+-638 93 1 151 27 225 101 99 98 129 219 85 339 -21 57 -49 87 -626 665 l-603
+605 5 70 c18 217 -117 451 -316 549 -127 62 -250 78 -379 51z m1696 -1906 c70
+-34 113 -125 93 -199 -31 -116 -158 -168 -262 -107 -77 45 -107 133 -73 214
+21 50 42 72 90 94 53 24 99 24 152 -2z"/>
+
+<path fill="#565656" d="M2183 2496 c-23 -7 -56 -23 -75 -34 -18 -12 -172 -161 -343 -331
+l-310 -311 45 -45 c24 -25 49 -45 55 -45 5 0 152 142 325 315 264 264 319 315
+340 313 19 -2 26 -10 28 -32 3 -26 -26 -59 -315 -348 l-318 -318 38 -37 37
+-38 319 318 c287 287 321 318 347 315 24 -2 30 -8 32 -32 3 -26 -26 -59 -315
+-348 l-318 -318 48 -47 47 -48 320 320 c225 225 327 333 342 365 31 66 30 159
+-4 225 -66 127 -210 198 -325 161z"/>
+
+<path fill="#36A9E1" d="M800 875 l-175 -175 -85 0 -85 0 -135 -222 c-74 -121 -137 -225 -139
+-231 -4 -11 89 -107 104 -107 5 0 109 60 230 134 l220 133 3 84 3 84 177 178
+177 177 -60 60 -60 60 -175 -175z"/>
+
+</g>
+</svg>
diff --git a/sdnr/wt/odlux/framework/src/assets/icons/User.svg b/sdnr/wt/odlux/framework/src/assets/icons/User.svg
new file mode 100644 (file)
index 0000000..99618cf
--- /dev/null
@@ -0,0 +1,21 @@
+<!-- highstreet technologies GmbH colour scheme \r
+       Grey    #565656\r
+       LBlue #36A9E1\r
+       DBlue #246DA2\r
+       Green #003F2C / #006C4B\r
+       Yellw #C8D400\r
+       Red     #D81036\r
+-->\r
+\r
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 600 600">\r
+\r
+  <defs>\r
+    <clipPath id="circular-border">\r
+      <circle cx="300" cy="300" r="250" />\r
+    </clipPath>\r
+  </defs>\r
+  \r
+  <circle fill="#36A9E1" cx="300" cy="300" r="280"/>\r
+  <circle fill="#ffffff" cx="300" cy="230" r="100" />\r
+  <circle fill="#ffffff" cx="300" cy="550" r="190" clip-path="url(#circular-border)" />\r
+</svg>
\ No newline at end of file
diff --git a/sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.png b/sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.png
new file mode 100644 (file)
index 0000000..412390c
Binary files /dev/null and b/sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.png differ
diff --git a/sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.svg b/sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.svg
new file mode 100644 (file)
index 0000000..c1d74bc
--- /dev/null
@@ -0,0 +1,81 @@
+<!-- highstreet technologies GmbH colour scheme 
+       Grey    #565656
+       LBlue #36A9E1
+       DBlue #246DA2
+       Green #003F2C / #006C4B
+       Yellw #C8D400
+       Red     #D81036
+-->
+
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 795.000000 139.000000">
+
+<g transform="translate(0.000000,139.000000) scale(0.100000,-0.100000)">
+
+<path fill="#565656" d="M355 1163 c-38 -2 -75 -9 -82 -15 -10 -8 -13 -53 -13 -175 0 -175 -9
+-215 -50 -226 -17 -4 -20 -13 -20 -59 0 -48 3 -56 32 -80 l33 -27 3 -190 c4
+-226 -4 -213 135 -209 l92 3 0 70 0 70 -32 3 -33 3 0 148 c0 135 -2 151 -21
+175 l-20 26 20 26 c19 24 21 40 21 170 l0 143 33 3 32 3 0 70 0 70 -30 1 c-16
+0 -61 -1 -100 -3z"/>
+
+<path fill="#565656" d="M7473 1154 c-2 -6 -3 -38 -1 -70 3 -59 3 -59 36 -62 l32 -3 0 -137
+c0 -119 3 -141 20 -169 20 -33 20 -33 0 -65 -17 -29 -20 -51 -20 -175 l0 -142
+-32 -3 -33 -3 0 -70 0 -70 97 -3 c141 -4 138 -9 138 212 0 96 3 182 6 191 4 9
+17 21 30 26 21 8 24 15 24 67 0 53 -3 60 -30 80 l-30 23 0 172 c0 123 -4 178
+-12 189 -16 20 -217 31 -225 12z"/>
+
+<path fill="#246DA2" d="M645 1138 c-3 -7 -4 -215 -3 -463 l3 -450 90 0 90 0 0 195 c0 215 5
+237 65 280 71 50 146 45 178 -13 14 -27 17 -64 19 -247 l2 -215 88 0 88 0 0
+250 0 250 -27 55 c-33 67 -71 97 -138 110 -89 17 -181 -12 -240 -75 l-25 -27
+-5 179 -5 178 -88 3 c-64 2 -89 -1 -92 -10z"/>
+
+<path fill="#C8D400" d="M2499 1111 c-150 -48 -271 -193 -301 -361 -32 -184 85 -406 259 -492
+165 -81 408 -24 514 120 31 43 31 44 13 61 -10 10 -45 33 -76 50 l-57 32 -15
+-28 c-75 -145 -315 -142 -403 6 -135 226 18 510 249 462 69 -15 114 -42 139
+-86 12 -19 26 -35 31 -35 22 0 138 91 132 103 -89 153 -301 227 -485 168z"/>
+
+<path fill="#246DA2" d="M1467 1094 c-4 -4 -7 -51 -7 -105 l0 -98 -42 -3 -43 -3 0 -70 0 -70
+43 -3 42 -3 0 -197 c0 -185 1 -201 23 -242 26 -53 89 -90 152 -90 45 0 171 32
+187 48 7 7 4 30 -9 74 -16 52 -23 62 -39 60 -73 -11 -101 -12 -114 -3 -12 8
+-16 43 -18 180 l-3 170 68 3 68 3 0 70 0 70 -67 3 -67 3 -3 102 -3 102 -80 3
+c-45 1 -84 0 -88 -4z"/>
+
+<path fill="#C8D400" d="M7017 1094 c-4 -4 -7 -51 -7 -105 l0 -98 -37 -3 -38 -3 0 -70 0 -70
+36 -3 36 -3 7 -67 c3 -37 6 -130 6 -208 0 -130 2 -144 23 -175 27 -41 98 -79
+147 -79 36 0 120 19 172 39 l26 10 -15 58 c-19 74 -28 85 -59 72 -35 -13 -88
+-11 -102 3 -8 8 -12 63 -12 180 l0 167 68 3 67 3 0 70 0 70 -67 3 -67 3 -3
+102 -3 102 -85 3 c-47 1 -89 0 -93 -4z"/>
+
+<path fill="#C8D400" d="M3349 890 c-63 -11 -123 -43 -174 -95 -55 -54 -82 -106 -94 -181 -25
+-161 50 -304 198 -372 77 -36 211 -38 285 -4 75 34 129 84 167 154 32 59 34
+69 34 158 0 82 -4 103 -26 151 -46 98 -138 168 -249 189 -59 11 -75 11 -141 0z
+m168 -178 c44 -31 67 -86 67 -157 0 -113 -53 -176 -153 -183 -70 -5 -115 22
+-148 88 -53 103 -11 233 87 273 31 13 117 1 147 -21z"/>
+
+<path fill="#C8D400" d="M4254 890 c-61 -13 -106 -34 -147 -69 l-36 -32 -3 48 -3 48 -80 0
+-80 0 0 -330 0 -330 85 0 85 0 5 202 c5 184 7 204 26 229 45 60 111 88 171 72
+54 -14 58 -31 63 -278 l5 -225 85 0 85 0 0 266 0 266 -30 48 c-46 74 -132 106
+-231 85z"/>
+
+<path fill="#C8D400" d="M5040 890 c-63 -11 -89 -23 -142 -63 l-47 -36 -3 47 -3 47 -80 0 -80
+0 0 -330 0 -330 90 0 90 0 1 196 c1 158 4 201 18 226 37 73 151 111 205 69 28
+-22 35 -68 40 -276 l6 -215 85 0 85 0 0 245 c-1 264 -7 307 -51 360 -47 55
+-122 76 -214 60z"/>
+
+<path fill="#C8D400" d="M5709 890 c-99 -17 -200 -94 -250 -190 -21 -39 -24 -59 -24 -145 0
+-90 3 -105 28 -153 79 -149 241 -220 407 -178 79 21 145 61 188 118 39 51 34
+58 -54 77 -56 13 -56 13 -88 -16 -17 -16 -44 -35 -59 -41 -42 -18 -117 -15
+-157 7 -33 17 -79 81 -80 108 0 10 60 13 253 15 l252 3 -1 70 c-2 211 -197
+364 -415 325z m151 -150 c36 -19 80 -79 80 -110 0 -6 -56 -10 -160 -10 -149 0
+-160 1 -160 19 0 29 43 85 80 103 46 24 112 23 160 -2z"/>
+
+<path fill="#C8D400" d="M6483 890 c-186 -39 -303 -204 -273 -386 19 -114 95 -214 200 -263
+46 -22 69 -26 145 -26 76 0 99 4 145 26 65 30 125 80 146 120 17 33 15 35 -84
+64 l-62 18 -28 -25 c-40 -38 -65 -48 -120 -48 -91 0 -162 80 -162 182 0 84 36
+148 99 174 65 27 133 15 176 -32 l25 -27 80 23 c44 13 83 26 85 30 6 10 -42
+71 -82 104 -68 58 -194 86 -290 66z"/>
+
+<path fill="#36A9E1" d="M1934 407 c-2 -7 -4 -52 -2 -98 l3 -84 70 0 70 0 0 95 0 95 -68 3
+c-50 2 -69 -1 -73 -11z"/>
+
+</g>
+</svg>
index a04ab16..d41d826 100644 (file)
@@ -15,7 +15,7 @@
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
+import  React from 'react';
 import { Theme } from '@mui/material/styles';
 
 import { WithStyles } from '@mui/styles';
@@ -31,7 +31,7 @@ import Typography from '@mui/material/Typography';
 
 import { ClearErrorInfoAction, RemoveErrorInfoAction } from '../actions/errorActions';
 
-import connect, { Connect } from '../flux/connect';
+import { connect, Connect } from '../flux/connect';
 
 const styles = (theme: Theme) => createStyles({
   modal: {
diff --git a/sdnr/wt/odlux/framework/src/components/icons/menuIcon.tsx b/sdnr/wt/odlux/framework/src/components/icons/menuIcon.tsx
new file mode 100644 (file)
index 0000000..0d7d734
--- /dev/null
@@ -0,0 +1,29 @@
+import React from 'react';
+
+type MenuIconPropsBase = {
+  className?: string;
+  size?: number | string;
+};
+
+type MenuIconPropsWithColor = MenuIconPropsBase & {
+  color: string;
+};
+
+type MenuIconProps = MenuIconPropsBase | MenuIconPropsWithColor;
+
+const MenuIcon = (props: MenuIconProps) => {
+  const { className, size = '30px' } = props;
+  const color = 'color' in props ? props.color : '#36A9E1';
+
+  return (
+    <svg className={className} width={size} height={size} viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink">
+      <path fill={color} d="M4,10h24c1.104,0,2-0.896,2-2s-0.896-2-2-2H4C2.896,6,2,6.896,2,8S2.896,10,4,10z" />
+      <path fill={color} d="M28,14H4c-1.104,0-2,0.896-2,2  s0.896,2,2,2h24c1.104,0,2-0.896,2-2S29.104,14,28,14z" />
+      <path fill={color} d="M28,22H4c-1.104,0-2,0.896-2,2s0.896,2,2,2h24c1.104,0,2-0.896,2-2  S29.104,22,28,22z" />
+    </svg>
+  );
+};
+
+MenuIcon.defaultName = 'MenuIcon';
+
+export default MenuIcon;
\ No newline at end of file
index b10cc8c..f2bb1f5 100644 (file)
@@ -42,7 +42,7 @@ import withStyles from '@mui/styles/withStyles';
 import createStyles from '@mui/styles/createStyles';
 
 
-import defaultLogo from '../assets/images/defaultLogo.svg';
+const defaultLogo = require('../assets/icons/ht.Connect.svg');
 
 const styles = (theme: Theme) => createStyles({
   headerLogo: {
@@ -91,7 +91,7 @@ class LogoComponent extends React.Component<LogoProps, ILogoState> {
       console.info([
         "Logo hidden, because browser window width (",
         this.state.windowWidth,
-        "px) is lower thershold (",
+        "px) is lower threshold (",
         this.hideLogoWhenWindowWidthIsLower,
         "px)."].join(''));
     }
index 8541cfe..c1a5005 100644 (file)
@@ -450,13 +450,13 @@ class MaterialTableComponent<TData extends {} = {}> extends React.Component<Mate
                   if (filterExpressionAsString.length === 0 || isNaN(valueAsNumber)) return true;
                   
                   if (filterExpressionAsString.startsWith('>=')) {
-                    return valueAsNumber >= Number(filterExpressionAsString.substr(2).trim());
+                    return valueAsNumber >= Number(filterExpressionAsString.substring(2).trim());
                   } else if (filterExpressionAsString.startsWith('<=')) {
-                    return valueAsNumber <= Number(filterExpressionAsString.substr(2).trim());
+                    return valueAsNumber <= Number(filterExpressionAsString.substring(2).trim());
                   } else if (filterExpressionAsString.startsWith('>')) {
-                    return valueAsNumber > Number(filterExpressionAsString.substr(1).trim());
+                    return valueAsNumber > Number(filterExpressionAsString.substring(1).trim());
                   } else if (filterExpressionAsString.startsWith('<')) {
-                    return valueAsNumber < Number(filterExpressionAsString.substr(1).trim());
+                    return valueAsNumber < Number(filterExpressionAsString.substring(1).trim());
                   }
                 } else if (column.type === ColumnType.date){
                    const valueAsString = String(dataValue);
@@ -480,13 +480,13 @@ class MaterialTableComponent<TData extends {} = {}> extends React.Component<Mate
                    const filterExpressionAsString = String(filterExpression).trim();             
 
                    if (filterExpressionAsString.startsWith('>=')) {
-                    return valueAsDate >= convertToDate(filterExpressionAsString.substr(2).trim());
+                    return valueAsDate >= convertToDate(filterExpressionAsString.substring(2).trim());
                   } else if (filterExpressionAsString.startsWith('<=')) {
-                    return valueAsDate <= convertToDate(filterExpressionAsString.substr(2).trim());
+                    return valueAsDate <= convertToDate(filterExpressionAsString.substring(2).trim());
                   } else if (filterExpressionAsString.startsWith('>')) {
-                    return valueAsDate > convertToDate(filterExpressionAsString.substr(1).trim());
+                    return valueAsDate > convertToDate(filterExpressionAsString.substring(1).trim());
                   } else if (filterExpressionAsString.startsWith('<')) {
-                    return valueAsDate < convertToDate(filterExpressionAsString.substr(1).trim());
+                    return valueAsDate < convertToDate(filterExpressionAsString.substring(1).trim());
                   }
 
                   
index f8ae6ea..ab0d465 100644 (file)
@@ -16,9 +16,9 @@
  * ============LICENSE_END==========================================================================
  */
 
-import { Button, Checkbox, FormControlLabel, MenuItem, Popover, Switch, Typography } from '@mui/material';
-import connect, { Connect, IDispatcher } from '../../flux/connect';
-import * as React from 'react';
+import React from 'react';
+import { Button, FormControlLabel, Popover, Switch, Typography } from '@mui/material';
+import { connect, Connect, IDispatcher } from '../../flux/connect';
 
 import { ColumnModel } from './columnModel';
 import { IApplicationStoreState } from '../../store/applicationStore';
index f901549..e2fda76 100644 (file)
@@ -51,6 +51,7 @@ export type ExternalMethodes<TData> = {
     onHandleChangeRowsPerPage: (rowsPerPage: number | null) => void;
     onHideColumns: (columnName: string[]) => void;
     onShowColumns: (columnName: string[]) => void;
+    onClearFilters: () => void;
   },
  createPreActions: (dispatch: Dispatch, skipRefresh?: boolean) => {
   onPreFilterChanged: (preFilter: {
@@ -328,7 +329,13 @@ export function createExternal<TData>(callback: DataCallback<TData>, selectState
         dispatch((dispatch: Dispatch) => {
           dispatch(new ShowColumnsAction(columnName));
         })
-      }
+      },
+      onClearFilters: () => {
+        dispatch((dispatch: Dispatch) => {
+          let filter = { };
+          dispatch(new SetFilterChangedAction(filter));
+        });
+      },
       // selected:
     };
   };
index 744cb0d..626cb89 100644 (file)
@@ -27,6 +27,8 @@ import { WithStyles } from '@mui/styles';
 import withStyles from '@mui/styles/withStyles';
 import createStyles from '@mui/styles/createStyles';
 import { toAriaLabel } from '../../utilities/yangHelper';
+import { IconType } from '../../models/iconDefinition';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 
 const styles = (theme: Theme) => createStyles({
   active: {
@@ -35,7 +37,7 @@ const styles = (theme: Theme) => createStyles({
 });
 
 export interface IListItemLinkProps extends WithStyles<typeof styles> {
-  icon: JSX.Element | null;
+  icon: IconType | null;
   primary: string | React.ComponentType;
   secondary?: React.ComponentType;
   to: string;
@@ -48,13 +50,19 @@ export const ListItemLink = withStyles(styles)((props: IListItemLinkProps) => {
   const renderLink = (itemProps: any): JSX.Element => (
     props.external ? <a target="_blank" href={to} { ...itemProps }></a> :
   <NavLink exact={ exact } to={ to } activeClassName={ classes.active } { ...itemProps } />);
-
+  
+  const customIconHeight = 22;
   const ariaLabel = typeof Primary === 'string' ? toAriaLabel("link-to-"+Primary) : toAriaLabel("link-to-"+Primary.displayName);
+  
+  //create menu icon, either using an faIcon or a link to a custom svg icon
+  //moved to one place for easier usage
+  const listItemIcon = icon && ( typeof icon === 'string' ? <img height={customIconHeight} src={icon} /> : <FontAwesomeIcon icon={icon} /> );
+    
   return (
        <>
         <ListItem button component={ renderLink } aria-label={ariaLabel}>
           { icon
-            ? <ListItemIcon>{ icon }</ListItemIcon>
+            ? <ListItemIcon>{ listItemIcon }</ListItemIcon>
             : null
           }
         { typeof Primary === 'string'
index 195706d..d969286 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
+import React from 'react';
 import { Theme } from '@mui/material/styles';
+import classNames from 'classnames';
 
 import { WithStyles } from '@mui/styles';
 import withStyles from '@mui/styles/withStyles';
 import createStyles from '@mui/styles/createStyles';
 
-import { faHome, faAddressBook } from '@fortawesome/free-solid-svg-icons';
-
 import Drawer from '@mui/material/Drawer';
 import List from '@mui/material/List';
 
@@ -34,11 +33,14 @@ import { faProjectDiagram } from '@fortawesome/free-solid-svg-icons';
 
 import ListItemLink from '../components/material-ui/listItemLink';
 
-import connect, { Connect } from '../flux/connect';
+import { connect, Connect } from '../flux/connect';
 import { MenuAction } from '../actions/menuAction';
-import * as classNames from 'classnames';
 import { transportPCEUrl } from '../app';
 
+const aboutIcon = require('../assets/icons/About.svg');
+const homeIcon = require('../assets/icons/Home.svg');
+const loginIcon = require('../assets/icons/User.svg');
+const settingsIcon = require('../assets/icons/Tools.svg');
 
 const drawerWidth = 240;
 
@@ -141,16 +143,15 @@ export const NavigationMenu = withStyles(styles)(connect()(({ classes, state, di
     window.dispatchEvent(new Event('menu-resized'));
   }, [isOpen])
 
-  let menuItems = state.framework.applicationRegistraion && Object.keys(state.framework.applicationRegistraion).map(key => {
-    const reg = state.framework.applicationRegistraion[key];
-    const icon = !reg.icon ? null :( typeof reg.icon === 'string' ? <img height={22} src={reg.icon} /> : <FontAwesomeIcon icon={reg.icon} /> )
+  let menuItems = state.framework.applicationRegistration && Object.keys(state.framework.applicationRegistration).map(key => {
+    const reg = state.framework.applicationRegistration[key];
     return reg && (
       <ListItemLink
         key={reg.name}
         to={reg.path || `/${reg.name}`}
         primary={reg.menuEntry || reg.name}
         secondary={reg.subMenuEntry}
-        icon={icon} />
+        icon={reg.icon || null} />
     ) || null;
   }) || null;
 
@@ -160,7 +161,7 @@ export const NavigationMenu = withStyles(styles)(connect()(({ classes, state, di
       key={"transportPCE"}
       to={transportUrl}
       primary={"TransportPCE"}
-      icon={<FontAwesomeIcon icon={faProjectDiagram} />}
+      icon={faProjectDiagram}
       external />;
 
     const linkFound = menuItems.find(obj => obj.key === "linkCalculation");
@@ -191,17 +192,17 @@ export const NavigationMenu = withStyles(styles)(connect()(({ classes, state, di
         <div className={classes.toolbar} />
         { /* https://fiffty.github.io/react-treeview-mui/ */}
         <List className={classes.menu} component="nav">
-          <ListItemLink exact to="/" primary="Home" icon={<FontAwesomeIcon icon={faHome} />} />
+          <ListItemLink exact to="/" primary="Home" icon={homeIcon} />
           <Divider />
           {
           menuItems
           }
           <Divider />
-          <ListItemLink to="/about" primary="About" icon={<FontAwesomeIcon icon={faAddressBook} />} />
+          <ListItemLink to="/about" primary="About" icon={aboutIcon} />
           {(false && process.env.NODE_ENV === "development")
             ? <>
               <Divider />
-              <ListItemLink to="/test" primary="Test" icon={<FontAwesomeIcon icon={faHome} />} />
+              <ListItemLink to="/test" primary="Test" icon={settingsIcon} />
             </>
             : null
           }
index d055b8a..aa22f17 100644 (file)
@@ -15,9 +15,9 @@
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
+import React from 'react';
 
-import connect, { Connect } from '../../flux/connect';
+import { connect, Connect } from '../../flux/connect';
 
 import { SetTitleAction } from '../../actions/titleActions';
 import { AddErrorInfoAction } from '../../actions/errorActions';
index 90f15c1..ffd516b 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
+import  React from 'react';
 import { Button, FormControlLabel, Switch, Typography } from '@mui/material';
 import makeStyles from '@mui/styles/makeStyles';
 import { SettingsComponentProps } from '../../models/settings';
-import * as React from 'react';
-import connect, { Connect, IDispatcher } from '../../flux/connect';
+import { connect, Connect, IDispatcher } from '../../flux/connect';
 import { IApplicationStoreState } from '../../store/applicationStore';
 import { getGeneralSettingsAction, SetGeneralSettingsAction, updateGeneralSettingsAction } from '../../actions/settingsAction';
 import { sendMessage, SettingsMessage } from '../../services/broadcastService';
index 19d3bdf..40c0fc7 100644 (file)
@@ -15,7 +15,7 @@
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
+import React from 'react';
 import { withRouter, RouteComponentProps } from 'react-router-dom';
 
 import { Theme } from '@mui/material/styles';
@@ -27,9 +27,6 @@ import Toolbar from '@mui/material/Toolbar';
 import Typography from '@mui/material/Typography';
 import Button from '@mui/material/Button';
 import IconButton from '@mui/material/IconButton';
-import Block from '@mui/icons-material/Block';
-import Adjust from '@mui/icons-material/Adjust';
-import MenuIcon from '@mui/icons-material/Menu';
 import AccountCircle from '@mui/icons-material/AccountCircle';
 import MenuItem from '@mui/material/MenuItem';
 import Menu from '@mui/material/Menu';
@@ -41,10 +38,12 @@ import { faDotCircle } from '@fortawesome/free-solid-svg-icons';
 import { logoutUser } from '../actions/authentication';
 import { PushAction, ReplaceAction } from '../actions/navigationActions';
 
-import connect, { Connect, IDispatcher } from '../flux/connect';
-import Logo from './logo';
+import { connect, Connect, IDispatcher } from '../flux/connect';
 import { MenuAction, MenuClosedByUser } from '../actions/menuAction';
 
+import MenuIcon from './icons/menuIcon';
+import Logo from './logo';
+
 const styles = (theme: Theme) => createStyles({
   appBar: {
     zIndex: theme.zIndex.drawer + 1,
@@ -58,7 +57,8 @@ const styles = (theme: Theme) => createStyles({
   },
   icon: {
     marginLeft: 16,
-    marginRight: 8
+    marginRight: 8,
+    marginBottom: -2,
   },
   connected: {
     color: "green"
@@ -112,10 +112,10 @@ class TitleBarComponent extends React.Component<TitleBarProps, { anchorEl: HTMLE
 
 
     // add notificationInfo element before help
-    if (state.framework.applicationRegistraion) {
+    if (state.framework.applicationRegistration) {
       let isNotificationInfoAdded = false;
-      Object.keys(state.framework.applicationRegistraion).map(key => {
-        const reg = state.framework.applicationRegistraion[key];
+      Object.keys(state.framework.applicationRegistration).map(key => {
+        const reg = state.framework.applicationRegistration[key];
         if (reg && reg.statusBarElement) {
           if (key === "help") {
             isNotificationInfoAdded = true;
@@ -132,7 +132,12 @@ class TitleBarComponent extends React.Component<TitleBarProps, { anchorEl: HTMLE
     }
 
     const stateIcon = state.framework.applicationState.icon;
-    const icon = !stateIcon ? null :( typeof stateIcon === 'string' ? <img className={classes.icon} height={22} src={stateIcon} /> : <FontAwesomeIcon className={classes.icon} icon={stateIcon} /> )
+    const customIconHeight = 22; 
+    const icon = !stateIcon
+      ? null
+      : (typeof stateIcon === 'string'
+        ? <img className={classes.icon} height={customIconHeight} src={stateIcon} />
+        : <FontAwesomeIcon className={classes.icon} icon={stateIcon} />)
     
 
     return (
similarity index 69%
rename from sdnr/wt/odlux/framework/src/flux/connect.ts
rename to sdnr/wt/odlux/framework/src/flux/connect.tsx
index f54e4e0..09d30da 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
-import * as PropTypes from 'prop-types';
+import React, { FC, useContext, createContext, useState, useEffect, useRef } from 'react';
 
-import { Dispatch } from '../flux/store';
+import { Dispatch } from './store';
 
 import { ApplicationStore, IApplicationStoreState } from '../store/applicationStore';
 
+const LogLevel = +(localStorage.getItem('log.odlux.framework.flux.connect') || 0);
+
 interface IApplicationStoreContext {
   applicationStore: ApplicationStore;
 }
@@ -38,12 +39,12 @@ interface IDispatchProps {
   dispatch: Dispatch;
 }
 
-type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
-
 type ComponentDecoratorInfer<TMergedProps> = {
   <TProps>(wrappedComponent: React.ComponentType<TProps & TMergedProps>): React.ComponentClass<Omit<TProps & TMergedProps, keyof TMergedProps>>;
 };
 
+const ApplicationStoreContext = createContext<IApplicationStoreContext | undefined>(undefined);
+
 export type Connect<TMapProps extends ((...args: any) => any) | undefined = undefined, TMapDispatch extends ((...args: any) => any) | undefined = undefined> =
   (TMapProps extends ((...args: any) => any) ? ReturnType<TMapProps> : IApplicationStoreProps) &
   (TMapDispatch extends ((...args: any) => any) ? ReturnType<TMapDispatch> : IDispatchProps);
@@ -75,9 +76,7 @@ export function connect<TProps, TStateProps, TDispatchProps>(
   const injectApplicationStore = (WrappedComponent: React.ComponentType<TProps & (IApplicationStoreProps | TStateProps) & IDispatchProps>): React.ComponentType<TProps> => {
 
     class StoreAdapter extends React.Component<TProps, {}> {
-      public static contextTypes = { ...WrappedComponent.contextTypes, applicationStore: PropTypes.object.isRequired };
-      context: IApplicationStoreContext;
-
+      
       render(): JSX.Element {
 
         if (isWrappedComponentIsVersion1(WrappedComponent)) {
@@ -112,7 +111,7 @@ export function connect<TProps, TStateProps, TDispatchProps>(
         this.forceUpdate();
       }
     }
-
+    StoreAdapter.contextType = ApplicationStoreContext;
     return StoreAdapter;
   }
 
@@ -138,24 +137,77 @@ export function connect<TProps, TStateProps, TDispatchProps>(
   }
 }
 
-interface ApplicationStoreProviderProps extends React.Props<ApplicationStoreProvider> {
+type ApplicationStoreProviderProps = {
   applicationStore: ApplicationStore;
 }
 
-export class ApplicationStoreProvider extends React.Component<ApplicationStoreProviderProps>
-  implements /* React.ComponentLifecycle<ApplicationStoreProviderProps, any>, */ React.ChildContextProvider<IApplicationStoreContext> {
+export const ApplicationStoreProvider: FC<ApplicationStoreProviderProps> = (props) => {
+  const { applicationStore, children } = props;
 
-  public static childContextTypes = { applicationStore: PropTypes.object.isRequired };
+  return (
+    <ApplicationStoreContext.Provider value={{ applicationStore }}>
+      {children}
+    </ApplicationStoreContext.Provider>
+  );
+};
 
-  getChildContext(): IApplicationStoreContext {
-    return {
-      applicationStore: this.props.applicationStore
-    };
+export const useApplicationStore = (): ApplicationStore => {
+  const context = useContext(ApplicationStoreContext);
+  if (context == null || context.applicationStore == null) {
+    throw new Error("Requires application store provider!")
   }
+  return context.applicationStore
+};
 
-  render(): JSX.Element {
-    return React.Children.only(this.props.children) as any; //type error, fix when possible
+export const useSelectApplicationState = <TProp extends unknown >( selector: (state: IApplicationStoreState) => TProp, eqFunc = (a: TProp, b: TProp) => a === b ): TProp => {
+  const context = useContext(ApplicationStoreContext);
+  if (context == null || context.applicationStore == null) {
+    throw new Error("Requires application store provider!")
   }
-}
+  
+  const [propState, setPropState] = useState<TProp>(selector(context.applicationStore.state));
+  
+  const selectorRef = useRef(selector);
+  selectorRef.current = selector;
+
+  const propStateRef = useRef({propState});
+  propStateRef.current.propState = propState;
+
+  useEffect(() => {
+    if (context == null || context.applicationStore == null) {
+      throw new Error("Requires application store provider!")
+    }
+
+    const changedHandler = () => {
+      const newState = selectorRef.current(context.applicationStore.state);
+      if (!eqFunc(newState, propStateRef.current.propState)) {
+        setPropState(newState);
+      }
+    };
+
+    if (LogLevel > 3) {
+      console.log("useSelectApplicationState: adding handler", changedHandler);
+    }
+
+    context.applicationStore.changed.addHandler(changedHandler);
+
+    return () => {
+      if (LogLevel > 3) {
+        console.log("useSelectApplicationState: removing handler", changedHandler);
+      }
 
-export default connect;
\ No newline at end of file
+      context.applicationStore.changed.removeHandler(changedHandler);
+    }
+  }, [context]);
+
+  return propState;
+  
+};
+
+export const useApplicationDispatch = (): Dispatch => {
+  const context = useContext(ApplicationStoreContext);
+  if (context == null || context.applicationStore == null) {
+    throw new Error("Requires application store provider!")
+  }
+  return context.applicationStore.dispatch;
+};
index dd80ce7..347d295 100644 (file)
@@ -20,6 +20,8 @@ import { Event } from "../common/event"
 import { Action } from './action';
 import { IActionHandler } from './action';
 
+const LogLevel = +(localStorage.getItem('log.odlux.framework.flux.store') || 0);
+
 export interface Dispatch {
   <TAction extends Action>(action: TAction): TAction;
 }
@@ -28,8 +30,8 @@ export interface Enhancer<TStoreState> {
   (store: Store<TStoreState>): Dispatch;
 }
 
-class InitialisationAction extends Action { };
-const initialisationAction = new InitialisationAction();
+class InitializationAction extends Action { };
+const initializationAction = new InitializationAction();
 
 export class Store<TStoreState> {
 
@@ -43,19 +45,22 @@ export class Store<TStoreState> {
 
     this._isDispatching = false;
      
-    this.changed = new Event<void>(); // sollten wir hier eventuell sogar den state mit übergeben ?
+    this.changed = new Event<void>();
 
     this._actionHandler = actionHandler;
     
     this._state = initialState as TStoreState;
     if (enhancer) this._dispatch = enhancer(this);
 
-    this._dispatch(initialisationAction);
+    this._dispatch(initializationAction);
   }
 
   public changed: Event<void>;
 
   private _dispatch: Dispatch = <TAction extends Action>(payload: TAction): TAction => {
+    if (LogLevel > 2) {
+      console.log('Store::Dispatch - ', payload);
+    }
     if (payload == null || !(payload instanceof Action)) {
       throw new Error(
         'Actions must inherit from type Action. ' +
@@ -76,6 +81,9 @@ export class Store<TStoreState> {
     }
 
     if (this._state !== oldState) {
+      if (LogLevel > 3) {
+        console.log('Store::Dispatch - state has changed', this._state);
+      }
       this.changed.invoke();
     }
 
index f98d774..71b9e33 100644 (file)
@@ -23,7 +23,7 @@ import { AuthPolicy, User } from '../models/authentication';
 import { onLogin, onLogout } from '../services/applicationApi';
 import { startWebsocketSession, endWebsocketSession } from '../services/notificationService';
 import { startUserSession, endUserSession } from '../services/userSessionService';
-import { getSettings } from '../services/settingsService';
+import { getUserdata } from '../services/userdataService';
 
 export interface IAuthenticationState {
   user?: User;
index 47725e6..fb0874f 100644 (file)
 import { Dispatch } from '../flux/store';
 import { MiddlewareApi } from '../store/applicationStore';
 
+const LogLevel = +(localStorage.getItem('log.odlux.framework.middleware.logger') || 0);
 
 function createLoggerMiddleware() {
   return function logger({ getState }: MiddlewareApi) {
     return (next: Dispatch): Dispatch => action => {
-      console.log('will dispatch', action);
+      if (LogLevel > 2) console.log('will dispatch', action);
       const returnValue = next(action);
-      console.log('state after dispatch', getState());
+      if (LogLevel > 2) console.log('state after dispatch', getState());
       return returnValue;
     };
-  }
+  };
 }
 
 export const logger = createLoggerMiddleware();
index 5f3eed5..44035fe 100644 (file)
@@ -55,7 +55,7 @@ const routerMiddlewareCreator = (history: History) => () => (next: Dispatch): Di
     // ensure user is logged in and token is valid
     if (action.pathname.startsWith("/oauth") && (action.search.startsWith("?token="))){
       const ind =  action.search.lastIndexOf("token=");
-      const tokenStr = ind > -1 ? action.search.substr(ind+6) : null;
+      const tokenStr = ind > -1 ? action.search.substring(ind+6) : null;
       const token = tokenStr && jwt.decode(tokenStr);
       if (tokenStr && token) {
         // @ts-ignore
index 36523f9..8246ee8 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import { GeneralSettings } from '../models/settings';
-import { setGeneralSettingsAction, SetGeneralSettingsAction } from '../actions/settingsAction';
+
 import { Event } from '../common/event';
 import { ApplicationStore } from '../store/applicationStore';
 import { AuthMessage, getBroadcastChannel, sendMessage } from './broadcastService';
-import { endWebsocketSession } from './notificationService';
-import { getSettings } from './settingsService';
 
 let resolveApplicationStoreInitialized: (store: ApplicationStore) => void;
 let applicationStore: ApplicationStore | null = null;
index 14e3ed0..bd620a0 100644 (file)
@@ -23,7 +23,7 @@ import { applicationApi } from './applicationApi';
 /** Represents registry to manage all applications. */
 class ApplicationManager {
     
-  /** Stores all registerd applications.  */
+  /** Stores all registered applications.  */
   private _applications: { [key: string]: ApplicationInfo }; 
   
   /** Initializes a new instance of this class. */
@@ -32,7 +32,7 @@ class ApplicationManager {
     this.changed = new Event<void>(); 
   }
 
-  /** The chaged event will fire if the registration has changed. */
+  /** The changed event will fire if the registration has changed. */
   public changed: Event<void>;
 
   /** Registers a new application. */
index f2c3ebc..40968e5 100644 (file)
@@ -22,89 +22,95 @@ import { ReplaceAction } from "../actions/navigationActions";
 import { User } from "../models/authentication";
 import { ApplicationStore } from "../store/applicationStore";
 
-type Broadcaster = {channel: BroadcastChannel, key: String};
+type Broadcaster = {
+  channel: BroadcastChannel;
+  key: String;
+};
 
 type AuthTypes = 'login' | 'logout';
-export type AuthMessage={key: AuthTypes, data: any};
+export type AuthMessage = {
+  key: AuthTypes;
+  data: any;
+};
 
 type SettingsType = 'general';
-export type SettingsMessage={key: SettingsType, enableNotifications: boolean, user: string};
+export type SettingsMessage = {
+  key: SettingsType;
+  enableNotifications: boolean;
+  user: string;
+};
 
-let channels: Broadcaster[] = [];
-let store : ApplicationStore | null = null;
+const channels: Broadcaster[] = [];
+let store: ApplicationStore | null = null;
 
 export const saveChannel = (channel: BroadcastChannel, channelName: string) => {
-    channels.push({channel: channel, key: channelName});
-}
-
-export const startBroadcastChannel = (applicationStore: ApplicationStore)=>{
-    store=applicationStore;
-
-    //might decide to use one general broadcast channel with more keys in the future
-    createAuthBroadcastChannel();
-    createSettingsBroadcastChannel();
-}
-
-const createSettingsBroadcastChannel = () =>{
-
-    const name = "odlux_settings";
-    const bc: BroadcastChannel = new BroadcastChannel(name);
-    channels.push({ channel: bc, key: name });
-
-    bc.onmessage = (eventMessage: MessageEvent<SettingsMessage>) => {
-        console.log(eventMessage)
-
-        if (eventMessage.data.key === 'general') {
-            
-            if (store?.state.framework.authenticationState.user) {
-               const data = eventMessage.data;
-               if(store.state.framework.authenticationState.user.user === data.user){
-                  store?.dispatch(setGeneralSettingsAction(data.enableNotifications));
-               }
-            }
-        }
-    }
+  channels.push({ channel: channel, key: channelName });
+};
 
-}
+export const startBroadcastChannel = (applicationStore: ApplicationStore) => {
+  store = applicationStore;
 
-const createAuthBroadcastChannel = () => {
-    const name = "odlux_auth";
-    const bc: BroadcastChannel = new BroadcastChannel(name);
-    channels.push({ channel: bc, key: name });
-
-    bc.onmessage = (eventMessage: MessageEvent<AuthMessage>) => {
-        console.log(eventMessage)
-
-        if (eventMessage.data.key === 'login') {
-            if (!store?.state.framework.authenticationState.user) {
-                const initialToken = localStorage.getItem("userToken");
-                if (initialToken) {
-                    store?.dispatch(loginUserAction(User.fromString(initialToken)));
-                    store?.dispatch(new ReplaceAction("/"));
-                }
-            }
-        }
-        else if (eventMessage.data.key === 'logout') {
+  //might decide to use one general broadcast channel with more keys in the future
+  createAuthBroadcastChannel();
+  createSettingsBroadcastChannel();
+};
 
-            if (store?.state.framework.authenticationState.user) {
-                store?.dispatch(logoutUser());
-                store?.dispatch(new ReplaceAction("/login"));
-            }
-        } 
-    }
-}
+const createSettingsBroadcastChannel = () => {
 
-export const getBroadcastChannel = (channelName: string) =>{
-    const foundChannel = channels.find(s =>s.key===channelName);
-    return foundChannel?.channel;
-}
+  const name = "odlux_settings";
+  const bc: BroadcastChannel = new BroadcastChannel(name);
+  channels.push({ channel: bc, key: name });
 
+  bc.onmessage = (eventMessage: MessageEvent<SettingsMessage>) => {
+    console.log(eventMessage);
 
-export const sendMessage = (data: any, channel: string) =>{
+    if (eventMessage.data.key === 'general') {
 
-       const foundChannel = channels.find(s =>s.key===channel);
-       if(foundChannel){
-        foundChannel.channel.postMessage(data);
-       }
+      if (store?.state.framework.authenticationState.user) {
+        const data = eventMessage.data;
+        if (store.state.framework.authenticationState.user.user === data.user) {
+          store?.dispatch(setGeneralSettingsAction(data.enableNotifications));
+        }
+      }
+    }
+  }
+};
+
+const createAuthBroadcastChannel = () => {
+  const name = "odlux_auth";
+  const bc: BroadcastChannel = new BroadcastChannel(name);
+  channels.push({ channel: bc, key: name });
+
+  bc.onmessage = (eventMessage: MessageEvent<AuthMessage>) => {
+    console.log(eventMessage)
+
+    if (eventMessage.data.key === 'login') {
+      if (!store?.state.framework.authenticationState.user) {
+        const initialToken = localStorage.getItem("userToken");
+        if (initialToken) {
+          store?.dispatch(loginUserAction(User.fromString(initialToken)));
+          store?.dispatch(new ReplaceAction("/"));
+        }
+      }
+    }
+    else if (eventMessage.data.key === 'logout') {
 
+      if (store?.state.framework.authenticationState.user) {
+        store?.dispatch(logoutUser());
+        store?.dispatch(new ReplaceAction("/login"));
+      }
     }
+  }
+};
+
+export const getBroadcastChannel = (channelName: string) => {
+  const foundChannel = channels.find(s => s.key === channelName);
+  return foundChannel?.channel;
+};
+
+export const sendMessage = (data: any, channel: string) => {
+  const foundChannel = channels.find(s => s.key === channel);
+  if (foundChannel) {
+    foundChannel.channel.postMessage(data);
+  }
+};
index 19b4513..85f0708 100644 (file)
@@ -18,5 +18,5 @@
 export { applicationManager } from './applicationManager';
 export { subscribe, unsubscribe } from './notificationService';
 export { requestRest } from './restService';
-export { putSettings, getSettings} from './settingsService';
+export { saveUserdata, getUserdata } from './userdataService';
 
index d727e4c..a296c52 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
+import { ReplaceAction } from '../actions/navigationActions';
+import { AddErrorInfoAction } from '../actions/errorActions';
 
-import { ApplicationStore } from "../store/applicationStore";
-import { ReplaceAction } from "../actions/navigationActions";
-import { AddErrorInfoAction } from "../actions/errorActions";
+import { storeService } from './storeService';
 
 const baseUri = `${ window.location.origin }`;
 const absUrlPattern = /^https?:\/\//;
-let applicationStore: ApplicationStore | null = null;
-
-export const startRestService = (store: ApplicationStore) => {
-  applicationStore = store;
-};
 
 export const formEncode = (params: { [key: string]: string | number }) => Object.keys(params).map((key) => {
   return encodeURIComponent(key) + '=' + encodeURIComponent(params[key].toString());
@@ -46,9 +41,9 @@ export const getAccessPolicyByUrl = (url: string) => {
     DELETE: false,
   };
   
-  if (!applicationStore) return result;
+  if (!storeService.applicationStore) return result;
 
-  const { state: { framework: { applicationState: { enablePolicy }, authenticationState: { policies }}} } = applicationStore!;
+  const { state: { framework: { applicationState: { enablePolicy }, authenticationState: { policies } } } } = storeService.applicationStore!;
   
   result.GET = true;
   result.POST = true;
@@ -71,7 +66,7 @@ export const getAccessPolicyByUrl = (url: string) => {
 
   return result;
 
-}
+};
 
 /** Sends a rest request to the given path. 
  * @returns The data, or null it there was any error
@@ -87,8 +82,8 @@ export async function requestRest<TData>(path: string = '', init: RequestInit =
 /** Sends a rest request to the given path and reports the server state. 
  *  @returns An object with the server state, a message and the data or undefined in case of a json parse error.
  */
-export async function requestRestExt<TData>(path: string = '', init: RequestInit = {}, authenticate: boolean = true, isResource: boolean = false): Promise<{ status: number, message?: string, data: TData | null | undefined }> {
-  const result: { status: number, message?: string, data: TData | null } = {
+export async function requestRestExt<TData>(path: string = '', init: RequestInit = {}, authenticate: boolean = true, isResource: boolean = false): Promise<{ status: number; message?: string; data: TData | null | undefined }> {
+  const result: { status: number; message?: string; data: TData | null } = {
     status: -1,
     data: null,
   };
@@ -100,60 +95,59 @@ export async function requestRestExt<TData>(path: string = '', init: RequestInit
     headers: {
       'Content-Type': 'application/json',
       'Accept': 'application/json',
-      ...init.headers
-    }
+      ...init.headers,
+    },
   };
-  if (!isAbsUrl && authenticate && applicationStore) {
-    const { state: { framework: { authenticationState: { user } } } } = applicationStore;
+  if (!isAbsUrl && authenticate && storeService.applicationStore) {
+    const { state: { framework: { authenticationState: { user } } } } = storeService.applicationStore;
     // do not request if the user is not valid
 
     if (!user || !user.isValid) {
       return {
         ...result,
-        message: "User is not valid or not logged in."
+        message: 'User is not valid or not logged in.',
       };
     }
     (init.headers = {
       ...init.headers,
-      'Authorization': `${user.tokenType} ${user.token}`
+      'Authorization': `${user.tokenType} ${user.token}`,
       //'Authorization': 'Basic YWRtaW46YWRtaW4='
     });
   }
 
   const fetchResult = await fetch(uri, init);
 
-  if(fetchResult.status === 403){
-    applicationStore && applicationStore.dispatch(new AddErrorInfoAction({title: "Forbidden", message:"Status: [403], access denied."}));
+  if (fetchResult.status === 403) {
+    storeService.applicationStore && storeService.applicationStore.dispatch(new AddErrorInfoAction({ title: 'Forbidden', message:'Status: [403], access denied.' }));
     return {
       ...result,
       status: 403,
-      message: "Forbidden."
+      message: 'Forbidden.',
     };
-  }
-  else if (fetchResult.status === 401) {
-    applicationStore && applicationStore.dispatch(new ReplaceAction(`/login?returnTo=${applicationStore.state.framework.navigationState.pathname}`));
+  } else if (fetchResult.status === 401) {
+    storeService.applicationStore && storeService.applicationStore.dispatch(new ReplaceAction(`/login?returnTo=${storeService.applicationStore.state.framework.navigationState.pathname}`));
     return {
       ...result,
       status: 401,
-      message: "Authentication requested by server."
+      message: 'Authentication requested by server.',
     };
   }
-  const contentType = fetchResult.headers.get("Content-Type") || fetchResult.headers.get("content-type");
-  const isJson = contentType && (contentType.toLowerCase().startsWith("application/json") || contentType.toLowerCase().startsWith("application/yang-data+json"));
+  const contentType = fetchResult.headers.get('Content-Type') || fetchResult.headers.get('content-type');
+  const isJson = contentType && (contentType.toLowerCase().startsWith('application/json') || contentType.toLowerCase().startsWith('application/yang-data+json'));
   try {
     const data = (isJson ? await fetchResult.json() : await fetchResult.text()) as TData;
     return {
       ...result,
       status: fetchResult.status,
       message: fetchResult.statusText,
-      data: data
+      data: data,
     };
   } catch (error) {
     return {
       ...result,
       status: fetchResult.status,
       message: error && error.message || String(error),
-      data: undefined
+      data: undefined,
     };
   }
 }
\ No newline at end of file
diff --git a/sdnr/wt/odlux/framework/src/services/storeService.ts b/sdnr/wt/odlux/framework/src/services/storeService.ts
new file mode 100644 (file)
index 0000000..cbb5987
--- /dev/null
@@ -0,0 +1,11 @@
+import { ApplicationStore } from "../store/applicationStore";
+
+let applicationStore: ApplicationStore | null = null;
+
+export const startSoreService = (store: ApplicationStore) => {
+  applicationStore = store;
+};
+
+export const storeService = { 
+  get applicationStore() { return applicationStore; },
+ };
\ No newline at end of file
@@ -22,7 +22,7 @@ import { requestRest } from "./restService";
     const settingsPath ="/userdata";
 
 
-    export function getSettings<TData>(partialPath?: string){
+    export function getUserdata<TData>(partialPath?: string){
        let path = settingsPath;
         if(partialPath){
             path+=partialPath
@@ -32,7 +32,7 @@ import { requestRest } from "./restService";
         return result;
     }
 
-    export function putSettings<TData>(partialPath: string, data: string){
+    export function saveUserdata<TData>(partialPath: string, data: string){
 
         const result = requestRest<TData>(settingsPath+partialPath, {method: "PUT", body: data})
         return result;
index a4545ef..cbe8c20 100644 (file)
@@ -37,7 +37,7 @@ import { updatePolicies } from '../middleware/policies';
 export type MiddlewareApi = MiddlewareArg<IApplicationStoreState>;
 
 export interface IFrameworkStoreState {
-  applicationRegistraion: IApplicationRegistration;
+  applicationRegistration: IApplicationRegistration;
   applicationState: IApplicationState;
   authenticationState: IAuthenticationState;
   navigationState: INavigationState;
@@ -48,7 +48,7 @@ export interface IApplicationStoreState {
 }
 
 const frameworkHandlers = combineActionHandler({
-  applicationRegistraion: applicationRegistryHandler,
+  applicationRegistration: applicationRegistryHandler,
   applicationState: applicationStateHandler,
   authenticationState: authenticationStateHandler,
   navigationState: navigationStateHandler
@@ -62,7 +62,7 @@ export const applicationStoreCreator = (): ApplicationStore => {
   const actionHandlers = Object.keys(applicationService.applications).reduce((acc, cur) => {
     const reg = applicationService.applications[cur];
     reg && typeof reg.rootActionHandler === 'function' && (acc[cur] = reg.rootActionHandler);
-    reg && +(reg.middlewares || 0) && middlewares.push(...(reg.middlewares as Middleware<IApplicationStoreState>[]));
+    reg && reg.middlewares && Array.isArray(reg.middlewares) && middlewares.push(...(reg.middlewares as Middleware<IApplicationStoreState>[]));
     return acc;
   }, { framework: frameworkHandlers } as any);
 
diff --git a/sdnr/wt/odlux/framework/src/utilities/logLevel.ts b/sdnr/wt/odlux/framework/src/utilities/logLevel.ts
new file mode 100644 (file)
index 0000000..a198d98
--- /dev/null
@@ -0,0 +1,8 @@
+export enum LogLevel {
+  Always = 0,
+  Error = 1,
+  Warning = 2,
+  Info = 3,
+  Debug = 4,
+  Trace = 5,
+}
index ac21970..937e74f 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
+import React, { FC, useEffect, useState } from 'react';
 import * as marked from 'marked';
 import * as hljs from 'highlight.js';
 import { requestRestExt } from '../services/restService';
 import { Button, Typography } from '@mui/material';
+
 const defaultRenderer = new marked.Renderer();
 defaultRenderer.link = (href, title, text) => (
   `<a target="_blank" rel="noopener noreferrer" href="${href}" title="${title}">${text}</a>`
 );
-interface AboutState {
-  content: string | null;
-  isCopiedSuccessfully: boolean;
-  isContentLoadedSucessfully: boolean;
-}
 
-type odluxVersion= {version:string,build:string, framework: string, 
+type OdluxVersion= {version:string,build:string, framework: string, 
   applications:{
     configurationApp: string,
     connectApp: string,
@@ -38,25 +34,27 @@ type odluxVersion= {version:string,build:string, framework: string,
     faultApp: string,
     helpApp: string,
     inventoryApp: string,
+    linkCalculationApp: string,
     maintenanceApp: string,
     mediatorApp: string,
+    networkMapApp: string,
     permanceHistoryApp: string
   }};
 
-type topologyVersion = {version: string, buildTimestamp: string};
-
-class AboutComponent extends React.Component<any, AboutState> {
-  textarea: React.RefObject<HTMLTextAreaElement>;
+type TopologyVersion = {version: string, buildTimestamp: string};
 
+const AboutComponent: FC = (props) => {
+  
+  const textareaRef = React.createRef<HTMLTextAreaElement>();
+  const [content, setContent] = useState<string | null>(null);
+  const [isCopiedSuccessfully, setCopySuccess] = useState(false);
+  const [isContetLoaded, setContentLoaded] = useState(false);
 
-  constructor(props: any) {
-    super(props);
-    this.state = { content: null, isCopiedSuccessfully:false, isContentLoadedSucessfully: false }
-    this.textarea = React.createRef();
-    this.loadAboutContent();
-  }
+  useEffect(()=>{
+    loadAboutContent();
+  },[]);
 
-  private getMarkOdluxVersionMarkdownTable(data:odluxVersion|null|undefined):string{
+  const getMarkOdluxVersionMarkdownTable = (data:OdluxVersion|null|undefined):string => {
     if(!data) {
       return "";
     }else{
@@ -72,6 +70,8 @@ class AboutComponent extends React.Component<any, AboutState> {
         `| InventoryApp | ${data.applications.inventoryApp}|\n `+
         `| EventLogApp | ${data.applications.eventLogApp}|\n `+
         `| MediatorApp | ${data.applications.mediatorApp}|\n `+
+        `| NetworkMapApp | ${data.applications.networkMapApp}|\n `+
+        `| LinkCalculatorApp | ${data.applications.linkCalculationApp}|\n `+
         `| HelpApp | ${data.applications.helpApp}|\n `;
       }
     
@@ -80,7 +80,7 @@ class AboutComponent extends React.Component<any, AboutState> {
     }
   }
 
-  private getTopologyVersionMarkdownTable(data: topologyVersion|null|undefined)
+  const getTopologyVersionMarkdownTable = (data: TopologyVersion|null|undefined) => 
     if(!data){
       return "No version";
     }
@@ -92,7 +92,7 @@ class AboutComponent extends React.Component<any, AboutState> {
     }
   }
 
-  private loadAboutContent(): void {
+  const loadAboutContent = (): void => {
     const baseUri = window.location.pathname.substring(0,window.location.pathname.lastIndexOf("/")+1);
     const init = {
       'method': 'GET',
@@ -102,7 +102,7 @@ class AboutComponent extends React.Component<any, AboutState> {
       }
     };
     const p1 = requestRestExt<string>('/about',init);
-    const p2 = requestRestExt<odluxVersion>(`${baseUri}version.json`);
+    const p2 = requestRestExt<OdluxVersion>(`${baseUri}version.json`);
     const p3 = requestRestExt<any>(`/topology/info/version`);
 
     Promise.all([p1,p2, p3]).then((responses) => {
@@ -110,31 +110,30 @@ class AboutComponent extends React.Component<any, AboutState> {
       const response2 = responses[1]; 
       const response3 = responses[2];   
       const content = response.status == 200 ? response.data : `${response.status} ${response.message}` || "Server error";
-      const content2 = `\n## ODLUX Version Info\n`+(response2.status == 200 ? this.getMarkOdluxVersionMarkdownTable(response2.data) : `${response2.message}` || "ODLUX Server error");
-      const content3 =  `\n## Topology API Version Info\n`+(response3.status == 200 ? this.getTopologyVersionMarkdownTable(response3.data): `Topology API not available`);
+      const content2 = `\n## ODLUX Version Info\n`+(response2.status == 200 ? getMarkOdluxVersionMarkdownTable(response2.data) : `${response2.message}` || "ODLUX Server error");
+      const content3 =  `\n## Topology API Version Info\n`+(response3.status == 200 ? getTopologyVersionMarkdownTable(response3.data): `Topology API not available`);
       const loadedSucessfully = response.status == 200 ? true : false;
-      this.setState({ content: (content + content2 + content3 ) || null, isContentLoadedSucessfully: loadedSucessfully });
+      setContent((content + content2 + content3 ) || null);
+      setContentLoaded(loadedSucessfully);
     }).catch((error) => {
-      this.setState({ content: error })
-    })
+      setContent(error);
+    });
   }
 
-  copyToClipboard = (e: React.MouseEvent<HTMLButtonElement>) =>{
+  const copyToClipboard = (e: React.MouseEvent<HTMLButtonElement>) =>{
     e.preventDefault();
 
-    if(this.textarea.current!==null){
-      this.textarea.current.select();
+    if(textareaRef.current!==null){
+      textareaRef.current.select();
       document.execCommand('copy');
       if(e.currentTarget != null){ // refocus on button, otherwhise the textarea would be focused
         e.currentTarget.focus();
       }
-      this.setState({isCopiedSuccessfully: true});
-      window.setTimeout(()=>{this.setState({isCopiedSuccessfully: false});},2000);
+      setCopySuccess(true);
+      window.setTimeout(()=>{ setCopySuccess(false);},2000);
     }
   }
 
-  render() {
-
     const markedOptions: marked.MarkedOptions = {
       gfm: true,
       breaks: false,
@@ -157,17 +156,17 @@ class AboutComponent extends React.Component<any, AboutState> {
     const style: React.CSSProperties = {};
     const containerStyle = { overflow: "auto", paddingRight: "20px" }
 
-    const html = (marked(this.state.content || 'loading', { renderer: markedOptions && markedOptions.renderer || defaultRenderer }));
+    const html = (marked(content || 'loading', { renderer: markedOptions && markedOptions.renderer || defaultRenderer }));
 
     return (
       <div style={containerStyle}>
-        { this.state.isContentLoadedSucessfully &&
+        { isContetLoaded &&
         <div style={{float: "right", marginRight: "10px"}}>
-        <Button aria-label="copy-version-information-button" color="inherit" variant="contained" onClick={e => this.copyToClipboard(e)}>
+        <Button aria-label="copy-version-information-button" color="inherit" variant="contained" onClick={e => copyToClipboard(e)}>
            Copy to clipboard
         </Button>
           {
-            this.state.isCopiedSuccessfully && 
+            isCopiedSuccessfully && 
             <Typography variant="body1" style={{color: "green"}} align="center">
              copied successfully
             </Typography>
@@ -183,13 +182,12 @@ class AboutComponent extends React.Component<any, AboutState> {
          <form>
           <textarea
            style={{opacity: ".01"}}
-            ref={this.textarea}
-            value={this.state.content || ''}
+            ref={textareaRef}
+            value={content || ''}
           />
         </form>
       </div>
     );
-  }
 };
 
 export const About = AboutComponent;
index 4676f5a..4a93cf0 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
+import React, { FC, memo } from 'react';
 import { HashRouter as Router, Route, Redirect, Switch } from 'react-router-dom';
 
 import { Theme } from '@mui/material/styles';
-import { WithStyles } from '@mui/styles';
-import withStyles from '@mui/styles/withStyles';
-import createStyles from '@mui/styles/createStyles';
-import { faHome, faAddressBook, faSignInAlt, faCog } from '@fortawesome/free-solid-svg-icons'
-
+import { makeStyles } from '@mui/styles';
 
 import { SnackbarProvider } from 'notistack';
 import { ConfirmProvider } from 'material-ui-confirm';
@@ -42,8 +38,14 @@ import UserSettings from '../views/settings';
 
 import applicationService from '../services/applicationManager';
 
+const aboutIcon = require('../assets/icons/About.svg');
+const homeIcon = require('../assets/icons/Home.svg');
+const loginIcon = require('../assets/icons/User.svg');
+const settingsIcon = require('../assets/icons/Tools.svg');
+
+const styles = makeStyles((theme: Theme) => {
 
-const styles = (theme: Theme) => createStyles({
+  return {
   root: {
     flexGrow: 1,
     height: '100%',
@@ -61,74 +63,69 @@ const styles = (theme: Theme) => createStyles({
     minWidth: 0, // So the Typography noWrap works
   },
   toolbar: theme.mixins.toolbar as any
+  };
 });
 
+const FrameComponent: FC = memo(() => {
 
+  const registrations = applicationService.applications;
+  const classes = styles();
+  return (
+    <ConfirmProvider>
+      <SnackbarProvider maxSnack={3}>
+        <Router>
+          <div className={classes.root}>
+            <SnackDisplay />
+            <ErrorDisplay />
+            <TitleBar />
+            <Menu />
+            <main className={classes.content}>
+              {
+                <div className={classes.toolbar} /> //needed for margins, don't remove!
+              }
+              <Switch>
+                <Route exact path="/" component={() => (
+                  <AppFrame title={"Home"} icon={homeIcon} >
+                    <Home />
+                  </AppFrame>
+                )} />
+                <Route path="/about" component={() => (
+                  <AppFrame title={"About"} icon={aboutIcon} >
+                    <About />
+                  </AppFrame>
+                )} />
+                <Route path="/settings" component={() => (
+                  <AppFrame title={"Settings"} icon={settingsIcon} >
+                    <UserSettings />
+                  </AppFrame>
+                )} />
+                {process.env.NODE_ENV === "development" ? <Route path="/test" component={() => (
+                  <AppFrame title={"Test"} icon={settingsIcon} >
+                    <Test />
+                  </AppFrame>
+                )} /> : null}
+                <Route path="/login" component={() => (
+                  <AppFrame title={"Login"} icon={loginIcon} >
+                    <Login />
+                  </AppFrame>
+                )} />
+                {Object.keys(registrations).map(p => {
+                  const application = registrations[p];
+                  return (<Route key={application.name} path={application.path || `/${application.name}`} component={() => (
+                    <AppFrame title={application.title || (typeof application.menuEntry === 'string' && application.menuEntry) || application.name} icon={application.icon} appId={application.name} >
+                      <application.rootComponent />
+                    </AppFrame>
+                  )} />)
+                })}
+                <Redirect to="/" />
+              </Switch>
+            </main>
+          </div>
+        </Router>
+      </SnackbarProvider>
+    </ConfirmProvider>
+  );
+});
 
-type FrameProps = WithStyles<typeof styles>;
-
-class FrameComponent extends React.Component<FrameProps>{
-
-  render() {
-    const registrations = applicationService.applications;
-    const { classes } = this.props;
-    return (
-      <ConfirmProvider>
-        <SnackbarProvider maxSnack={3}>
-            <Router>
-            <div className={classes.root}>
-                <SnackDisplay />
-                <ErrorDisplay />
-                <TitleBar />
-                <Menu />
-                <main className={classes.content}>
-                {
-                    <div className={classes.toolbar} /> //needed for margins, don't remove!
-                }
-                <Switch>
-                    <Route exact path="/" component={() => (
-                      <AppFrame title={"Home"} icon={faHome} >
-                          <Home />
-                      </AppFrame>
-                    )} />
-                    <Route path="/about" component={() => (
-                      <AppFrame title={"About"} icon={faAddressBook} >
-                          <About />
-                      </AppFrame>
-                    )} />
-                    <Route path="/settings" component={() => (
-                      <AppFrame title={"Settings"} icon={faCog} >
-                          <UserSettings />
-                      </AppFrame>
-                    )} />
-                    {process.env.NODE_ENV === "development" ? <Route path="/test" component={() => (
-                      <AppFrame title={"Test"} icon={faAddressBook} >
-                          <Test />
-                      </AppFrame>
-                    )} /> : null}
-                    <Route path="/login" component={() => (
-                      <AppFrame title={"Login"} icon={faSignInAlt} >
-                          <Login />
-                      </AppFrame>
-                      )} />
-                    { Object.keys(registrations).map(p => {
-                      const application = registrations[p];
-                      return (<Route key={application.name} path={application.path || `/${application.name}`} component={() => (
-                          <AppFrame title={application.title || (typeof application.menuEntry === 'string' && application.menuEntry) || application.name} icon={application.icon} appId={application.name} >
-                          <application.rootComponent />
-                          </AppFrame>
-                      )} />)
-                    })}
-                    <Redirect to="/" />
-                </Switch>
-                </main>
-            </div>
-            </Router>
-        </SnackbarProvider>
-      </ConfirmProvider>  
-    );
-  }
-}
-
-export const Frame = withStyles(styles)(FrameComponent);
+export const Frame = FrameComponent;
 export default Frame;
index 92fd0b2..72c5059 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
-import * as React from 'react';
-import { IApplicationStoreState } from "../store/applicationStore";
-import connect, { Connect, IDispatcher } from "../flux/connect";
+import React, {FC, useState} from 'react';
 import applicationService from '../services/applicationManager';
 
-type props = Connect<typeof mapProps, typeof mapDispatch>;
 
-type SettingsEntry = { name: string, element: JSX.Element }
+type DashboardElement = { name: string, element: JSX.Element };
 
-
-const mapProps = (state: IApplicationStoreState) => ({
-});
-
-const mapDispatch = (dispatcher: IDispatcher) => ({
-});
-
-const DashboardView: React.FunctionComponent<props> = (props) => {
+const DashboardView: FC = (props) => {
 
   const registrations = applicationService.applications;
 
-  const [selectedIndex] = React.useState(0);
+  const [selectedIndex] = useState(0);
 
-  let settingsArray: SettingsEntry[] = [];
+  let dashboardArray: DashboardElement[] = [];
 
-  let settingsElements: (SettingsEntry)[] = Object.keys(registrations).map(p => {
+  let dashboardElements: (DashboardElement)[] = Object.keys(registrations).map(p => {
     const application = registrations[p];
 
     if (application.dashbaordElement) {
-      const value: SettingsEntry = { name: application.menuEntry?.toString()!, element: <application.dashbaordElement /> };
+      const value: DashboardElement = { name: application.menuEntry?.toString()!, element: <application.dashbaordElement /> };
       return value;
 
     } else {
       return null;
     }
-  }).filter((x): x is SettingsEntry => x !== null);
-
+  }).filter((x): x is DashboardElement => x !== null);
 
-  settingsArray.push(...settingsElements);
+  dashboardArray.push(...dashboardElements);
 
   return <div>
     <div>
       <div>
         {
-          settingsArray[selectedIndex]?.element
+          dashboardArray[selectedIndex]?.element
         }
       </div>
     </div>
@@ -67,4 +56,4 @@ const DashboardView: React.FunctionComponent<props> = (props) => {
 }
 
 
-export default connect(mapProps, mapDispatch)(DashboardView);
+export default DashboardView;
\ No newline at end of file
index e037edf..46c0872 100644 (file)
  * the License.
  * ============LICENSE_END==========================================================================
  */
-import * as React from 'react';
-import { withRouter, RouteComponentProps } from 'react-router-dom';
+import React, { FC, useEffect, useState } from 'react';
+import { RouteComponentProps, withRouter } from 'react-router-dom';
 
 import Alert from '@mui/material/Alert';
 import Avatar from '@mui/material/Avatar';
 import Button from '@mui/material/Button';
 import CssBaseline from '@mui/material/CssBaseline';
 import FormControl from '@mui/material/FormControl';
-import FormControlLabel from '@mui/material/FormControlLabel';
-import Checkbox from '@mui/material/Checkbox';
 import Input from '@mui/material/Input';
 import InputLabel from '@mui/material/InputLabel';
-import LockIcon from '@mui/icons-material/LockOutlined';
 import Paper from '@mui/material/Paper';
-import Typography from '@mui/material/Typography';
 import { Theme } from '@mui/material/styles';
+import Typography from '@mui/material/Typography';
 
-import { WithStyles } from '@mui/styles';
-import withStyles from '@mui/styles/withStyles';
-import createStyles from '@mui/styles/createStyles';
+import { makeStyles } from '@mui/styles';
 
-import connect, { Connect, IDispatcher } from '../flux/connect';
+import { useApplicationDispatch, useSelectApplicationState } from '../flux/connect';
 import authenticationService from '../services/authenticationService';
 
-import { updateExternalLoginProviderAsyncActionCreator } from '../actions/loginProvider';
 import { loginUserAction, UpdatePolicies } from '../actions/authentication';
+import { updateExternalLoginProviderAsyncActionCreator } from '../actions/loginProvider';
 
-import { IApplicationStoreState } from '../store/applicationStore';
 import { AuthPolicy, AuthToken, User } from '../models/authentication';
-import Menu from '@mui/material/Menu';
-import { MenuItem } from '@mui/material';
 
-const styles = (theme: Theme) => createStyles({
+const loginIcon = require('../assets/icons/User.svg');
+
+const styles = makeStyles((theme: Theme) =>{
+  return{
   layout: {
     width: 'auto',
     display: 'block', // Fix IE11 issue.
@@ -91,204 +86,165 @@ const styles = (theme: Theme) => createStyles({
      padding: '0 10px',
      color: 'grey'
   }
+};
 });
 
-const mapProps = (state: IApplicationStoreState) => ({
-  search: state.framework.navigationState.search,
-  authentication: state.framework.applicationState.authentication,
-  externalLoginProviders: state.framework.applicationState.externalLoginProviders ,
-});
-
-const mapDispatch = (dispatcher: IDispatcher) => ({
-  updateExternalProviders: () => dispatcher.dispatch(updateExternalLoginProviderAsyncActionCreator()),
-  updateAuthentication: (token: AuthToken | null) => {
-    const user = token && new User(token) || undefined;
-    dispatcher.dispatch(loginUserAction(user));
-  },
-  updatePolicies: (policies?: AuthPolicy[]) => {
-    return dispatcher.dispatch(new UpdatePolicies(policies));
-  },
-});
-
-type LoginProps = RouteComponentProps<{}> & WithStyles<typeof styles> & Connect<typeof mapProps, typeof mapDispatch>;
-
-interface ILoginState {
-  externalProviderAnchor: HTMLElement | null;
-  busy: boolean;
-  username: string;
-  password: string;
-  scope: string;
-  message: string;
-  isServerReady: boolean;
-  providers: {
-    id: string;
-    title: string;
-    loginUrl: string;
-  }[] | null;
-}
 
+type LoginProps = RouteComponentProps;
 
 // todo: ggf. redirect to einbauen
-class LoginComponent extends React.Component<LoginProps, ILoginState> {
+const LoginComponent:  FC<LoginProps> = (props) => {
 
-  constructor(props: LoginProps) {
-    super(props);
-
-    this.state = {
-      externalProviderAnchor: null,
-      busy: false,
-      username: '',
-      password: '',
-      scope: 'sdn',
-      message: '',
-      providers: null,
-      isServerReady: false
-    };
+  const search = useSelectApplicationState(state => state.framework.navigationState.search);
+  const authentication = useSelectApplicationState(state => state.framework.applicationState.authentication);
+  const externalLoginProviders = useSelectApplicationState(state => state.framework.applicationState.externalLoginProviders);
+  
+  const dispatch = useApplicationDispatch();
+  const updateExternalProviders = () => dispatch(updateExternalLoginProviderAsyncActionCreator());
+  const updateAuthentication = (token: AuthToken | null) => {
+    const user = token && new User(token) || undefined;
+    dispatch(loginUserAction(user));
+  }
+  const updatePolicies = (policies?: AuthPolicy[]) => {
+    return dispatch(new UpdatePolicies(policies));
   }
 
-  async componentDidMount(){
-     if (this.props.authentication === "oauth" && (this.props.externalLoginProviders == null || this.props.externalLoginProviders.length === 0)){
-       this.props.updateExternalProviders();
+  const [isBusy, setBusy] = useState(false);
+  const [username, setUsername] = useState("");
+  const [password, setPassword] = useState("");
+  const [scope, setScope] = useState("sdn");
+  const [message, setMessage] = useState("");
+  const [isServerReady, setIsServerReady] = useState(false);
+
+  useEffect(()=>{
+     if (authentication === "oauth" && (externalLoginProviders == null || externalLoginProviders.length === 0)){
+       updateExternalProviders();
      }
 
     authenticationService.getServerReadyState().then(result =>{
-      this.setState({isServerReady: result});
+      setIsServerReady(result);
     })
+  },[]);
 
-
-
-  }
-
-  private setExternalProviderAnchor = (el: HTMLElement | null) => {
-    this.setState({externalProviderAnchor: el })
-  }
-
-  render(): JSX.Element {
-    const { classes } = this.props;
-    const areProvidersAvailable = this.props.externalLoginProviders && this.props.externalLoginProviders.length > 0;
-    return (
-      <>
-        <CssBaseline />
-        <main className={classes.layout}>
-          <Paper className={classes.paper}>
-            <Avatar className={classes.avatar}>
-              <LockIcon />
-            </Avatar>
-            <Typography variant="caption">Sign in</Typography>
-            <form className={classes.form}>
-
-
-              {areProvidersAvailable &&
-                <>
-                  {
-                    this.props.externalLoginProviders!.map((provider, index) => (
-                      <Button
-                        aria-controls="externalLogin"
-                        aria-label={"external-login-identity-provider-" + (index + 1)}
-                        aria-haspopup="true"
-                        fullWidth
-                        variant="contained"
-                        color="inherit"
-                        className={classes.submit} onClick={() => { window.location = provider.loginUrl as any; }}>
-                        {provider.title}
-                      </Button>))
-                  }
-
-                  <div className={classes.lineContainer}>
-                    <span className={classes.thirdPartyDivider}>
-                      OR
-                    </span>
-                  </div>
-                </>
-              }
-
-              <FormControl variant="standard" margin="normal" required fullWidth>
-                <InputLabel htmlFor="username">Username</InputLabel>
-                <Input id="username" name="username" autoComplete="username" autoFocus
-                  disabled={this.state.busy}
-                  value={this.state.username}
-                  onChange={event => { this.setState({ username: event.target.value }) }} />
-              </FormControl>
-              <FormControl variant="standard" margin="normal" required fullWidth>
-                <InputLabel htmlFor="password">Password</InputLabel>
-                <Input
-                  name="password"
-                  type="password"
-                  id="password"
-                  autoComplete="current-password"
-                  disabled={this.state.busy}
-                  value={this.state.password}
-                  onChange={event => { this.setState({ password: event.target.value }) }}
-                />
-              </FormControl>
-              <FormControl variant="standard" margin="normal" required fullWidth>
-                <InputLabel htmlFor="password">Domain</InputLabel>
-                <Input
-                  name="scope"
-                  type="scope"
-                  id="scope"
-                  disabled={this.state.busy}
-                  value={this.state.scope}
-                  onChange={event => { this.setState({ scope: event.target.value }) }}
-                />
-              </FormControl>
-              <Button
-                aria-label="login-button"
-                type="submit"
-                fullWidth
-                variant="contained"
-                color="inherit"
-                disabled={this.state.busy}
-                className={classes.submit}
-                onClick={this.onSignIn}
-              >
-                Sign in
-              </Button>
-
-            </form>
-            {this.state.message && <Alert severity="error">{this.state.message}</Alert>}
-          </Paper>
-        </main>
-      </>
-    );
-  }
-
-  private onSignIn = async (event: React.MouseEvent<HTMLButtonElement>) => {
+  const onSignIn = async (event: React.MouseEvent<HTMLButtonElement>) => {
     event.preventDefault();
+  
+    setBusy(true);
 
-    this.setState({ busy: true });
-
-    const token = this.props.authentication === "oauth" 
-      ? await authenticationService.authenticateUserOAuth(this.state.username, this.state.password, this.state.scope)
-      : await authenticationService.authenticateUserBasicAuth(this.state.username, this.state.password, this.state.scope); 
+    const token = authentication === "oauth" 
+      ? await authenticationService.authenticateUserOAuth(username, password, scope)
+      : await authenticationService.authenticateUserBasicAuth(username, password, scope); 
 
-    this.props.updateAuthentication(token);
-    this.setState({ busy: false });
+    updateAuthentication(token);
+    setBusy(false);
 
     if (token) {
-      const query = this.props.search && this.props.search.replace(/^\?/, "").split('&').map(e => e.split("="));
+      const query = search && search.replace(/^\?/, "").split('&').map(e => e.split("="));
       const returnTo = query && query.find(e => e[0] === "returnTo");
-      this.props.history.replace(returnTo && returnTo[1] || "/");
+      props.history.replace(returnTo && returnTo[1] || "/");
     }
     else {
 
-      if(!this.state.isServerReady){
+      if(!isServerReady){
         const ready = await authenticationService.getServerReadyState();
         if(ready){
-          this.setState({isServerReady: true});
+          setIsServerReady(true);
         }else{
-          this.setState({message: "Login is currently not possible. Please re-try in a few minutes. If the problem persits, ask your administrator for assistence."});
+          setMessage("Login is currently not possible. Please re-try in a few minutes. If the problem persists, ask your administrator for assistance.");
         }
   
       }else{
-        this.setState({
-          message: "Could not log in. Please check your credentials or ask your administrator for assistence.",
-          password: ""
-        })
+        setMessage("Could not log in. Please check your credentials or ask your administrator for assistance.");
+        setPassword("");
       }
     }
   }
+  
+  const classes = styles();
+  const areProvidersAvailable = externalLoginProviders && externalLoginProviders.length > 0;
+
+  return (
+    <>
+      <CssBaseline />
+      <main className={classes.layout}>
+        <Paper className={classes.paper}>
+          <Avatar className={classes.avatar}>
+            <img src={loginIcon} alt="loginIcon" />
+          </Avatar>
+          <Typography variant="caption">Sign in</Typography>
+          <form className={classes.form}>
+            {areProvidersAvailable &&
+              <>
+                {
+                  externalLoginProviders!.map((provider, index) => (
+                    <Button
+                      aria-controls="externalLogin"
+                      aria-label={"external-login-identity-provider-" + (index + 1)}
+                      aria-haspopup="true"
+                      fullWidth
+                      variant="contained"
+                      color="inherit"
+                      className={classes.submit} onClick={() => { window.location = provider.loginUrl as any; }}>
+                      {provider.title}
+                    </Button>))
+                }
+                <div className={classes.lineContainer}>
+                  <span className={classes.thirdPartyDivider}>
+                    OR
+                  </span>
+                </div>
+              </>
+            }
+            <FormControl variant="standard" margin="normal" required fullWidth>
+              <InputLabel htmlFor="username">Username</InputLabel>
+              <Input id="username" name="username" autoComplete="username" autoFocus
+                disabled={isBusy}
+                value={username}
+                onChange={event => { setUsername(event.target.value); }} />
+            </FormControl>
+            <FormControl variant="standard" margin="normal" required fullWidth>
+              <InputLabel htmlFor="password">Password</InputLabel>
+              <Input
+                name="password"
+                type="password"
+                id="password"
+                autoComplete="current-password"
+                disabled={isBusy}
+                value={password}
+                onChange={event => { setPassword(event.target.value); }}
+              />
+            </FormControl>
+            <FormControl variant="standard" margin="normal" required fullWidth>
+              <InputLabel htmlFor="password">Domain</InputLabel>
+              <Input
+                name="scope"
+                type="scope"
+                id="scope"
+                disabled={isBusy}
+                value={scope}
+                onChange={event => { setScope(event.target.value); }}
+              />
+            </FormControl>
+            <Button
+              aria-label="login-button"
+              type="submit"
+              fullWidth
+              variant="contained"
+              color="inherit"
+              disabled={isBusy}
+              className={classes.submit}
+              onClick={onSignIn}
+            >
+              Sign in
+            </Button>
+
+          </form>
+          {message && <Alert severity="error">{message}</Alert>}
+        </Paper>
+      </main>
+    </>
+  );
 }
 
-export const Login = withStyles(styles)(withRouter(connect(mapProps, mapDispatch)(LoginComponent)));
+export const Login = withRouter(LoginComponent);
 export default Login;
\ No newline at end of file
index a6b940b..5973db9 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
-import * as React from 'react';
-import { IApplicationStoreState } from "../store/applicationStore";
-import connect, { Connect, IDispatcher } from "../flux/connect";
+import React, {FC, useState } from 'react';
+import { useApplicationDispatch } from "../flux/connect";
 
-import applicationService from '../services/applicationManager';
-import { makeStyles } from '@mui/styles';
 import { Divider, List, ListItem, ListItemText, Paper } from '@mui/material';
+import { makeStyles } from '@mui/styles';
+import applicationService from '../services/applicationManager';
 
-import { GeneralUserSettings } from '../components/settings/general'
 import { GoBackAction } from '../actions/navigationActions';
+import { GeneralUserSettings } from '../components/settings/general';
 import { toAriaLabel } from '../utilities/yangHelper';
 
-type props = Connect<typeof mapProps, typeof mapDispatch>;
-
-type SettingsEntry = { name: string, element: JSX.Element }
-
-
-const mapProps = (state: IApplicationStoreState) => ({
-
-});
-
-const mapDispatch = (dispatcher: IDispatcher) => ({
-  goBack: () => dispatcher.dispatch(new GoBackAction())
-});
+type SettingsEntry = { name: string, element: JSX.Element };
 
 const styles = makeStyles({
   sectionMargin: {
@@ -47,7 +35,6 @@ const styles = makeStyles({
     marginBottom: "15px"
   },
   elementMargin: {
-
     marginLeft: "10px"
   },
   menu: {
@@ -55,15 +42,17 @@ const styles = makeStyles({
   }
 });
 
-const UserSettings: React.FunctionComponent<props> = (props) => {
+const UserSettings: FC = (props) => {
 
-  const classes = styles();
-  const registrations = applicationService.applications;
+  const dispatch = useApplicationDispatch();
+  const goBack = () => dispatch(new GoBackAction());
+
+  const [selectedIndex, setSelectedIndex] = useState(0);
 
-  const [selectedIndex, setSelectedIndex] = React.useState(0);
+  const registrations = applicationService.applications;
 
   const navigateBack = () => {
-    props.goBack();
+    goBack();
   }
 
   let settingsArray: SettingsEntry[] = [];
@@ -71,7 +60,6 @@ const UserSettings: React.FunctionComponent<props> = (props) => {
   //add all framework specific settings
   settingsArray.push({name:"General", element: <GeneralUserSettings onClose={navigateBack} />})
 
-
   //get app settings
   let settingsElements : (SettingsEntry) [] = Object.keys(registrations).map(p => {
     const application = registrations[p];
@@ -93,6 +81,8 @@ const UserSettings: React.FunctionComponent<props> = (props) => {
     setSelectedIndex(newValue);
   }
 
+  const classes = styles();
+
   return <div style={{ display: "flex", flexDirection: "row", height: "100%" }}>
    <div style={{ display: "flex", flexDirection: "column", height: "100%", width: "15%" }}>
       <Paper variant="outlined" style={{ height: "70%" }}>
@@ -101,7 +91,7 @@ const UserSettings: React.FunctionComponent<props> = (props) => {
             settingsArray.map((el, index) => {
               return (
               <>
-                <ListItem selected={selectedIndex === index} button onClick={e => { onSelectElement(e, index) }} aria-label={toAriaLabel(el?.name+"-settings")}>
+                <ListItem key={"settings-key-"+index} selected={selectedIndex === index} button onClick={e => { onSelectElement(e, index) }} aria-label={toAriaLabel(el?.name+"-settings")}>
                   <ListItemText primary={el?.name} style={{ padding: 0 }} />
                 </ListItem>
                 <Divider />
@@ -110,7 +100,6 @@ const UserSettings: React.FunctionComponent<props> = (props) => {
           }
         </List>
       </Paper>
-
     </div>
     <div style={{ height: "100%", width: "80%", marginLeft: 15 }}>
       <div style={{ height: "100%" }}>
@@ -123,4 +112,4 @@ const UserSettings: React.FunctionComponent<props> = (props) => {
 }
 
 
-export default connect(mapProps, mapDispatch)(UserSettings);
+export default UserSettings;
\ No newline at end of file
index f9e9746..1d9af90 100644 (file)
@@ -9,8 +9,10 @@
         "faultApp":"##odlux.apps.faultApp.buildno##",
         "helpApp":"##odlux.apps.helpApp.buildno##",
         "inventoryApp":"##odlux.apps.inventoryApp.buildno##",
+        "linkCalculationApp":"##odlux.apps.linkCalculationApp.buildno##",
         "maintenanceApp":"##odlux.apps.maintenanceApp.buildno##",
         "mediatorApp":"##odlux.apps.mediatorApp.buildno##",
+        "networkMapApp":"##odlux.apps.networkMapApp.buildno##",
         "permanceHistoryApp":"##odlux.apps.permanceHistoryApp.buildno##"
     
     }
index 33aa48d..795f1f1 100644 (file)
@@ -19,7 +19,9 @@
   ~ ============LICENSE_END=======================================================
   ~
   -->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
     <parent>
index 8a49bde..c356533 100644 (file)
@@ -1,10 +1,10 @@
-odlux.framework.buildno=166.7b222ec(22/08/26)
-odlux.apps.configurationApp.buildno=165.93e23ca(22/08/12)
-odlux.apps.connectApp.buildno=166.7b222ec(22/08/26)
-odlux.apps.eventLogApp.buildno=142.63ceae1(22/01/31)
-odlux.apps.faultApp.buildno=158.f3ad6e0(22/07/18)
-odlux.apps.helpApp.buildno=166.7b222ec(22/08/26)
-odlux.apps.inventoryApp.buildno=161.f556fbb(22/07/29)
-odlux.apps.maintenanceApp.buildno=142.63ceae1(22/01/31)
-odlux.apps.mediatorApp.buildno=158.f3ad6e0(22/07/18)
+odlux.framework.buildno=172.52348b7c(23/02/16)
+odlux.apps.configurationApp.buildno=172.52348b7c(23/02/16)
+odlux.apps.connectApp.buildno=172.52348b7c(23/02/16)
+odlux.apps.eventLogApp.buildno=172.52348b7c(23/02/16)
+odlux.apps.faultApp.buildno=177.3f75df5a(23/03/06)
+odlux.apps.helpApp.buildno=172.52348b7c(23/02/16)
+odlux.apps.inventoryApp.buildno=178.3bd8a2a9(23/03/16)
+odlux.apps.maintenanceApp.buildno=172.52348b7c(23/02/16)
+odlux.apps.mediatorApp.buildno=172.52348b7c(23/02/16)
 odlux.apps.permanceHistoryApp.buildno=81.1c38886(20/12/04)
index d65555e..ac9bfb5 100644 (file)
@@ -42,6 +42,7 @@
     "react": "17.0.2",
     "react-dom": "17.0.2",
     "react-router-dom": "5.2.0",
+    "react-split-pane": "0.1.92",
     "react-transition-group": "4.3.0"
   },
   "devDependencies": {
     "@babel/preset-react": "7.0.0",
     "@octokit/core": "3.0.0",
     "@types/jest": "23.3.12",
+    "@typescript-eslint/eslint-plugin": "5.42.0",
+    "@typescript-eslint/parser": "5.42.0",
     "autoprefixer": "9.1.5",
     "babel-loader": "8.0.4",
     "copy-webpack-plugin": "4.5.2",
     "css-loader": "1.0.0",
+    "eslint": "8.26.0",
+    "eslint-config-airbnb": "19.0.4",
+    "eslint-config-airbnb-typescript": "17.0.0",
+    "eslint-plugin-import": "2.26.0",
+    "eslint-plugin-jsx-a11y": "6.6.1",
+    "eslint-plugin-react": "7.31.10",
+    "eslint-plugin-react-hooks": "4.6.0",
     "extract-text-webpack-plugin": "next",
     "file-loader": "2.0.0",
     "glob-to-regexp": "0.4.1",
     "html-webpack-include-assets-plugin": "1.0.5",
     "html-webpack-plugin": "3.2.0",
     "jest": "23.6.0",
-    "less": "3.8.1",
-    "less-loader": "4.1.0",
-    "node-sass": "4.12.0",
     "postcss-loader": "3.0.0",
     "requirejs": "2.3.6",
     "requirejs-webpack-plugin": "1.0.5",
     "rimraf": "2.6.2",
-    "sass-loader": "7.1.0",
     "style-loader": "0.23.0",
     "terser-webpack-plugin": "1.2.1",
     "ts-jest": "23.10.5",
index 2d91a4b..6897cd4 100644 (file)
@@ -19,6 +19,7 @@
   ~ ============LICENSE_END=======================================================
   ~
   -->
+
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
diff --git a/sdnr/wt/odlux/proxy.conf.js b/sdnr/wt/odlux/proxy.conf.js
new file mode 100644 (file)
index 0000000..e5b91b6
--- /dev/null
@@ -0,0 +1,106 @@
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2021 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==========================================================================
+ */
+
+module.exports = {
+  "/about": {
+    target: "http://sdncweb:8080",
+    secure: false
+  },
+  "/yang-schema/": {
+    target: "http://sdncweb:8080",
+    secure: false
+  },
+  "/oauth/": {
+    target: "http://sdncweb:8080",
+    secure: false
+  },
+  "/database/": {
+    target: "http://sdncweb:8080",
+    secure: false
+  },
+  "/restconf/": {
+    target: "http://sdncweb:8080",
+    secure: false
+  },
+  "/rests/": {
+    target: "http://sdncweb:8080",
+    secure: false
+  },
+  "/userdata": {
+    target: "http://sdncweb:8080",
+    secure: false
+  },
+  "/userdata/": {
+    target: "http://sdncweb:8080",
+    secure: false
+  },
+  "/help/": {
+    target: "http://sdncweb:8080",
+    secure: false
+  },
+  "/about/": {
+    target: "http://sdncweb:8080",
+    secure: false
+  },
+  "/tree/": {
+    target: "http://sdncweb:8080",
+    secure: false
+  },
+  "/sitedoc/": {
+    target: "http://sdncweb:8080",
+    secure: false
+  },
+  "/topology/": {
+    target: "http://sdncweb:8080",
+    secure: false
+  },
+
+  "/websocket": {
+    target: "http://sdncweb:8080",
+    ws: true,
+    changeOrigin: true,
+    secure: false
+  },
+  "/apidoc": {
+    target: "http://sdncweb:8080",
+    ws: true,
+    changeOrigin: true,
+    secure: false
+  },
+  "/tiles/": {
+    target: "http://sdncweb:8080",
+    headers: {
+      "Connection": "keep-alive"
+    },
+    secure: false
+  },
+  "/swagger/": {
+    target: "http://swagger.t1.lab.osn-lab.com",
+    secure: false,
+    pathRewrite(pathname) {
+      return pathname.replace(/^\/swagger/, '/');
+    }
+  },
+  "/electromagnetic-field/": {
+    target: "http://sdncweb:8080",
+    ws: true,
+    changeOrigin: true,
+    secure: false
+  },
+}
+
index 243411d..3c87e83 100644 (file)
     "@babel/plugin-transform-react-jsx-self" "^7.0.0"
     "@babel/plugin-transform-react-jsx-source" "^7.0.0"
 
+"@babel/runtime-corejs3@^7.10.2":
+  version "7.21.0"
+  resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.21.0.tgz#6e4939d9d9789ff63e2dc58e88f13a3913a24eba"
+  integrity sha512-TDD4UJzos3JJtM+tHX+w2Uc+KWj7GV+VKKFdMVd2Rx8sdA19hcc3P3AHFYd5LVOw+pYuSd5lICC3gm52B6Rwxw==
+  dependencies:
+    core-js-pure "^3.25.1"
+    regenerator-runtime "^0.13.11"
+
 "@babel/runtime@^7.1.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.13.10", "@babel/runtime@^7.16.3", "@babel/runtime@^7.7.2":
   version "7.16.3"
   resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.3.tgz#b86f0db02a04187a3c17caa77de69840165d42d5"
   dependencies:
     regenerator-runtime "^0.13.4"
 
+"@babel/runtime@^7.10.2", "@babel/runtime@^7.18.9":
+  version "7.21.0"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673"
+  integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==
+  dependencies:
+    regenerator-runtime "^0.13.11"
+
 "@babel/runtime@^7.19.0":
   version "7.20.1"
   resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.1.tgz#1148bb33ab252b165a06698fde7576092a78b4a9"
   resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz#ea89004119dc42db2e1dba0f97d553f7372f6fcb"
   integrity sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg==
 
+"@eslint/eslintrc@^1.3.3":
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.4.1.tgz#af58772019a2d271b7e2d4c23ff4ddcba3ccfb3e"
+  integrity sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==
+  dependencies:
+    ajv "^6.12.4"
+    debug "^4.3.2"
+    espree "^9.4.0"
+    globals "^13.19.0"
+    ignore "^5.2.0"
+    import-fresh "^3.2.1"
+    js-yaml "^4.1.0"
+    minimatch "^3.1.2"
+    strip-json-comments "^3.1.1"
+
 "@evocateur/libnpmaccess@^3.1.2":
   version "3.1.2"
   resolved "https://registry.yarnpkg.com/@evocateur/libnpmaccess/-/libnpmaccess-3.1.2.tgz#ecf7f6ce6b004e9f942b098d92200be4a4b1c845"
   dependencies:
     prop-types "^15.7.2"
 
+"@humanwhocodes/config-array@^0.11.6":
+  version "0.11.8"
+  resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9"
+  integrity sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==
+  dependencies:
+    "@humanwhocodes/object-schema" "^1.2.1"
+    debug "^4.1.1"
+    minimatch "^3.0.5"
+
+"@humanwhocodes/module-importer@^1.0.1":
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c"
+  integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
+
+"@humanwhocodes/object-schema@^1.2.1":
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
+  integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
+
 "@lerna/add@3.21.0":
   version "3.21.0"
   resolved "https://registry.yarnpkg.com/@lerna/add/-/add-3.21.0.tgz#27007bde71cc7b0a2969ab3c2f0ae41578b4577b"
     prop-types "^15.7.2"
     react-is "^17.0.2"
 
+"@nodelib/fs.scandir@2.1.5":
+  version "2.1.5"
+  resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
+  integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==
+  dependencies:
+    "@nodelib/fs.stat" "2.0.5"
+    run-parallel "^1.1.9"
+
+"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
+  integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
+
 "@nodelib/fs.stat@^1.1.2":
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b"
   integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==
 
+"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8":
+  version "1.2.8"
+  resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
+  integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
+  dependencies:
+    "@nodelib/fs.scandir" "2.1.5"
+    fastq "^1.6.0"
+
 "@octokit/auth-token@^2.4.0":
   version "2.4.5"
   resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.5.tgz#568ccfb8cb46f36441fac094ce34f7a875b197f3"
   resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.3.10.tgz#d0afaec7ee55f591992e74c607df5dc7cd9c76ab"
   integrity sha512-W2bE8pGh9Tsg8mxh+B6BSH8lTG6ZV7K2ZMAlEwSTqKFU1wMI5HShyRKSp3DngnxCmDu35tW3RAC4mxBFYRsTuw==
 
+"@types/json-schema@^7.0.9":
+  version "7.0.11"
+  resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
+  integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
+
+"@types/json5@^0.0.29":
+  version "0.0.29"
+  resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
+  integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
+
 "@types/jsonwebtoken@7.2.8":
   version "7.2.8"
   resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-7.2.8.tgz#8d199dab4ddb5bba3234f8311b804d2027af2b3a"
   resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
   integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
 
+"@types/semver@^7.3.12":
+  version "7.3.13"
+  resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91"
+  integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==
+
 "@types/x2js@^3.1.0":
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/@types/x2js/-/x2js-3.1.0.tgz#d809ef1ace1a8b55b3e8cb6a1a0b282af66475be"
   dependencies:
     x2js "*"
 
+"@typescript-eslint/eslint-plugin@5.42.0":
+  version "5.42.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.42.0.tgz#36a8c0c379870127059889a9cc7e05c260d2aaa5"
+  integrity sha512-5TJh2AgL6+wpL8H/GTSjNb4WrjKoR2rqvFxR/DDTqYNk6uXn8BJMEcncLSpMbf/XV1aS0jAjYwn98uvVCiAywQ==
+  dependencies:
+    "@typescript-eslint/scope-manager" "5.42.0"
+    "@typescript-eslint/type-utils" "5.42.0"
+    "@typescript-eslint/utils" "5.42.0"
+    debug "^4.3.4"
+    ignore "^5.2.0"
+    natural-compare-lite "^1.4.0"
+    regexpp "^3.2.0"
+    semver "^7.3.7"
+    tsutils "^3.21.0"
+
+"@typescript-eslint/parser@5.42.0":
+  version "5.42.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.42.0.tgz#be0ffbe279e1320e3d15e2ef0ad19262f59e9240"
+  integrity sha512-Ixh9qrOTDRctFg3yIwrLkgf33AHyEIn6lhyf5cCfwwiGtkWhNpVKlEZApi3inGQR/barWnY7qY8FbGKBO7p3JA==
+  dependencies:
+    "@typescript-eslint/scope-manager" "5.42.0"
+    "@typescript-eslint/types" "5.42.0"
+    "@typescript-eslint/typescript-estree" "5.42.0"
+    debug "^4.3.4"
+
+"@typescript-eslint/scope-manager@5.42.0":
+  version "5.42.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.42.0.tgz#e1f2bb26d3b2a508421ee2e3ceea5396b192f5ef"
+  integrity sha512-l5/3IBHLH0Bv04y+H+zlcLiEMEMjWGaCX6WyHE5Uk2YkSGAMlgdUPsT/ywTSKgu9D1dmmKMYgYZijObfA39Wow==
+  dependencies:
+    "@typescript-eslint/types" "5.42.0"
+    "@typescript-eslint/visitor-keys" "5.42.0"
+
+"@typescript-eslint/type-utils@5.42.0":
+  version "5.42.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.42.0.tgz#4206d7192d4fe903ddf99d09b41d4ac31b0b7dca"
+  integrity sha512-HW14TXC45dFVZxnVW8rnUGnvYyRC0E/vxXShFCthcC9VhVTmjqOmtqj6H5rm9Zxv+ORxKA/1aLGD7vmlLsdlOg==
+  dependencies:
+    "@typescript-eslint/typescript-estree" "5.42.0"
+    "@typescript-eslint/utils" "5.42.0"
+    debug "^4.3.4"
+    tsutils "^3.21.0"
+
+"@typescript-eslint/types@5.42.0":
+  version "5.42.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.42.0.tgz#5aeff9b5eced48f27d5b8139339bf1ef805bad7a"
+  integrity sha512-t4lzO9ZOAUcHY6bXQYRuu+3SSYdD9TS8ooApZft4WARt4/f2Cj/YpvbTe8A4GuhT4bNW72goDMOy7SW71mZwGw==
+
+"@typescript-eslint/typescript-estree@5.42.0":
+  version "5.42.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.42.0.tgz#2592d24bb5f89bf54a63384ff3494870f95b3fd8"
+  integrity sha512-2O3vSq794x3kZGtV7i4SCWZWCwjEtkWfVqX4m5fbUBomOsEOyd6OAD1qU2lbvV5S8tgy/luJnOYluNyYVeOTTg==
+  dependencies:
+    "@typescript-eslint/types" "5.42.0"
+    "@typescript-eslint/visitor-keys" "5.42.0"
+    debug "^4.3.4"
+    globby "^11.1.0"
+    is-glob "^4.0.3"
+    semver "^7.3.7"
+    tsutils "^3.21.0"
+
+"@typescript-eslint/utils@5.42.0":
+  version "5.42.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.42.0.tgz#f06bd43b9a9a06ed8f29600273240e84a53f2f15"
+  integrity sha512-JZ++3+h1vbeG1NUECXQZE3hg0kias9kOtcQr3+JVQ3whnjvKuMyktJAAIj6743OeNPnGBmjj7KEmiDL7qsdnCQ==
+  dependencies:
+    "@types/json-schema" "^7.0.9"
+    "@types/semver" "^7.3.12"
+    "@typescript-eslint/scope-manager" "5.42.0"
+    "@typescript-eslint/types" "5.42.0"
+    "@typescript-eslint/typescript-estree" "5.42.0"
+    eslint-scope "^5.1.1"
+    eslint-utils "^3.0.0"
+    semver "^7.3.7"
+
+"@typescript-eslint/visitor-keys@5.42.0":
+  version "5.42.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.42.0.tgz#ee8d62d486f41cfe646632fab790fbf0c1db5bb0"
+  integrity sha512-QHbu5Hf/2lOEOwy+IUw0GoSCuAzByTAWWrOTKzTzsotiUnWFpuKnXcAhC9YztAf2EElQ0VvIK+pHJUPkM0q7jg==
+  dependencies:
+    "@typescript-eslint/types" "5.42.0"
+    eslint-visitor-keys "^3.3.0"
+
 "@webassemblyjs/ast@1.7.11":
   version "1.7.11"
   resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.7.11.tgz#b988582cafbb2b095e8b556526f30c90d057cace"
@@ -2380,6 +2548,11 @@ acorn-globals@^4.1.0:
     acorn "^6.0.1"
     acorn-walk "^6.0.1"
 
+acorn-jsx@^5.3.2:
+  version "5.3.2"
+  resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
+  integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
+
 acorn-walk@^6.0.1:
   version "6.2.0"
   resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c"
@@ -2395,6 +2568,11 @@ acorn@^6.0.1:
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6"
   integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==
 
+acorn@^8.8.0:
+  version "8.8.2"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a"
+  integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==
+
 agent-base@4, agent-base@^4.3.0:
   version "4.3.0"
   resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee"
@@ -2426,7 +2604,7 @@ ajv-keywords@^3.1.0:
   resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
   integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
 
-ajv@^6.1.0, ajv@^6.12.3:
+ajv@^6.1.0, ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4:
   version "6.12.6"
   resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
   integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
@@ -2436,11 +2614,6 @@ ajv@^6.1.0, ajv@^6.12.3:
     json-schema-traverse "^0.4.1"
     uri-js "^4.2.2"
 
-amdefine@>=0.0.4:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
-  integrity sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==
-
 ansi-colors@^3.0.0:
   version "3.2.4"
   resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf"
@@ -2471,6 +2644,11 @@ ansi-regex@^4.1.0:
   resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
   integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
 
+ansi-regex@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
+  integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
+
 ansi-styles@^2.2.1:
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
@@ -2483,6 +2661,13 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1:
   dependencies:
     color-convert "^1.9.0"
 
+ansi-styles@^4.1.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
+  integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
+  dependencies:
+    color-convert "^2.0.1"
+
 any-promise@^1.0.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
@@ -2536,6 +2721,19 @@ argparse@^1.0.7:
   dependencies:
     sprintf-js "~1.0.2"
 
+argparse@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
+  integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
+
+aria-query@^4.2.2:
+  version "4.2.2"
+  resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-4.2.2.tgz#0d2ca6c9aceb56b8977e9fed6aed7e15bbd2f83b"
+  integrity sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==
+  dependencies:
+    "@babel/runtime" "^7.10.2"
+    "@babel/runtime-corejs3" "^7.10.2"
+
 arr-diff@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf"
@@ -2558,6 +2756,14 @@ arr-union@^3.1.0:
   resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4"
   integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=
 
+array-buffer-byte-length@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead"
+  integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==
+  dependencies:
+    call-bind "^1.0.2"
+    is-array-buffer "^3.0.1"
+
 array-differ@^2.0.3:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-2.1.0.tgz#4b9c1c3f14b906757082925769e8ab904f4801b1"
@@ -2588,6 +2794,17 @@ array-ify@^1.0.0:
   resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece"
   integrity sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=
 
+array-includes@^3.1.4, array-includes@^3.1.5:
+  version "3.1.6"
+  resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f"
+  integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.4"
+    es-abstract "^1.20.4"
+    get-intrinsic "^1.1.3"
+    is-string "^1.0.7"
+
 array-union@^1.0.1, array-union@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
@@ -2595,6 +2812,11 @@ array-union@^1.0.1, array-union@^1.0.2:
   dependencies:
     array-uniq "^1.0.1"
 
+array-union@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
+  integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
+
 array-uniq@^1.0.1:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
@@ -2610,12 +2832,32 @@ array-unique@^0.3.2:
   resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
   integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
 
+array.prototype.flat@^1.2.5:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2"
+  integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.4"
+    es-abstract "^1.20.4"
+    es-shim-unscopables "^1.0.0"
+
+array.prototype.flatmap@^1.3.0:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183"
+  integrity sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.4"
+    es-abstract "^1.20.4"
+    es-shim-unscopables "^1.0.0"
+
 arrify@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
   integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=
 
-asap@^2.0.0, asap@~2.0.3:
+asap@^2.0.0:
   version "2.0.6"
   resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
   integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==
@@ -2655,6 +2897,11 @@ assign-symbols@^1.0.0:
   resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
   integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=
 
+ast-types-flow@^0.0.7:
+  version "0.0.7"
+  resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad"
+  integrity sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==
+
 astral-regex@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9"
@@ -2665,11 +2912,6 @@ async-each@^1.0.1:
   resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf"
   integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==
 
-async-foreach@^0.1.3:
-  version "0.1.3"
-  resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542"
-  integrity sha512-VUeSMD8nEGBWaZK4lizI1sf3yEC7pnAQ/mrI7pC2fBz2s/tq5jWWEngTwaf0Gruu/OoXRGLGg1XFqpYBiGTYJA==
-
 async-limiter@~1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
@@ -2709,6 +2951,11 @@ autoprefixer@9.1.5:
     postcss "^7.0.2"
     postcss-value-parser "^3.2.3"
 
+available-typed-arrays@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7"
+  integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==
+
 aws-sign2@~0.7.0:
   version "0.7.0"
   resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
@@ -2719,6 +2966,16 @@ aws4@^1.8.0:
   resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
   integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
 
+axe-core@^4.4.3:
+  version "4.6.3"
+  resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.6.3.tgz#fc0db6fdb65cc7a80ccf85286d91d64ababa3ece"
+  integrity sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg==
+
+axobject-query@^2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be"
+  integrity sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==
+
 babel-code-frame@^6.22.0, babel-code-frame@^6.26.0:
   version "6.26.0"
   resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
@@ -2973,13 +3230,6 @@ bindings@^1.5.0:
   dependencies:
     file-uri-to-path "1.0.0"
 
-block-stream@*:
-  version "0.0.9"
-  resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a"
-  integrity sha512-OorbnJVPII4DuUKbjARAe8u8EfqOmkEEaSFIyoQ7OjTHn6kafxWl0wLgoZ2rXaYd7MyLcDaU4TmhfxtwgcccMQ==
-  dependencies:
-    inherits "~2.0.0"
-
 bluebird@^3.5.1, bluebird@^3.5.3, bluebird@^3.5.5:
   version "3.7.2"
   resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
@@ -3061,7 +3311,7 @@ braces@^2.3.1, braces@^2.3.2:
     split-string "^3.0.2"
     to-regex "^3.0.1"
 
-braces@~3.0.2:
+braces@^3.0.2, braces@~3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
   integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
@@ -3391,11 +3641,6 @@ camelcase@^2.0.0:
   resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f"
   integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=
 
-camelcase@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
-  integrity sha512-4nhGqUkc4BqbBBB4Q6zLuD7lzzrHYrjKGeYaEji/3tFR5VdJu9v+LilhGIVe8wxEJPPOeWo7eg8dwY13TZ1BNg==
-
 camelcase@^4.1.0:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
@@ -3423,7 +3668,7 @@ caseless@~0.12.0:
   resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
   integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
 
-chalk@^1.1.1, chalk@^1.1.3:
+chalk@^1.1.3:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
   integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=
@@ -3443,6 +3688,14 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.4.1, chalk@^2.4
     escape-string-regexp "^1.0.5"
     supports-color "^5.3.0"
 
+chalk@^4.0.0:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
+  integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
+  dependencies:
+    ansi-styles "^4.1.0"
+    supports-color "^7.1.0"
+
 chardet@^0.7.0:
   version "0.7.0"
   resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
@@ -3572,15 +3825,6 @@ cli-width@^2.0.0:
   resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48"
   integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==
 
-cliui@^3.2.0:
-  version "3.2.0"
-  resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d"
-  integrity sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==
-  dependencies:
-    string-width "^1.0.1"
-    strip-ansi "^3.0.1"
-    wrap-ansi "^2.0.0"
-
 cliui@^4.0.0:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49"
@@ -3599,16 +3843,6 @@ cliui@^5.0.0:
     strip-ansi "^5.2.0"
     wrap-ansi "^5.1.0"
 
-clone-deep@^2.0.1:
-  version "2.0.2"
-  resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-2.0.2.tgz#00db3a1e173656730d1188c3d6aced6d7ea97713"
-  integrity sha512-SZegPTKjCgpQH63E+eN6mVEEPdQBOUzjyJm5Pora4lrwWRFS8I0QAxV/KD6vV/i0WuijHZWQC1fMsPEdxfdVCQ==
-  dependencies:
-    for-own "^1.0.0"
-    is-plain-object "^2.0.4"
-    kind-of "^6.0.0"
-    shallow-clone "^1.0.0"
-
 clone-deep@^4.0.1:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387"
@@ -3623,11 +3857,6 @@ clone@^1.0.2:
   resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
   integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4=
 
-clone@^2.1.1, clone@^2.1.2:
-  version "2.1.2"
-  resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f"
-  integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==
-
 clsx@^1.1.0, clsx@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188"
@@ -3663,12 +3892,19 @@ color-convert@^1.9.0, color-convert@^1.9.3:
   dependencies:
     color-name "1.1.3"
 
+color-convert@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+  integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+  dependencies:
+    color-name "~1.1.4"
+
 color-name@1.1.3:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
   integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
 
-color-name@^1.0.0:
+color-name@^1.0.0, color-name@~1.1.4:
   version "1.1.4"
   resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
   integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
@@ -3789,6 +4025,11 @@ config-chain@^1.1.11:
     ini "^1.3.4"
     proto-list "~1.2.1"
 
+confusing-browser-globals@^1.0.10:
+  version "1.0.11"
+  resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz#ae40e9b57cdd3915408a2805ebd3a5585608dc81"
+  integrity sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==
+
 connect-history-api-fallback@^1.3.0:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc"
@@ -3952,6 +4193,11 @@ copy-webpack-plugin@4.5.2:
     p-limit "^1.0.0"
     serialize-javascript "^1.4.0"
 
+core-js-pure@^3.25.1:
+  version "3.29.1"
+  resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.29.1.tgz#1be6ca2b8772f6b4df7fc4621743286e676c6162"
+  integrity sha512-4En6zYVi0i0XlXHVz/bi6l1XDjCqkKRq765NXuX+SnaIatlE96Odt5lMLjdxUiNI1v9OXI5DSLWYPlmTfkTktg==
+
 core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.7, core-js@^2.6.5:
   version "2.6.12"
   resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
@@ -4019,14 +4265,6 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7:
     safe-buffer "^5.0.1"
     sha.js "^2.4.8"
 
-cross-spawn@^3.0.0:
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982"
-  integrity sha512-eZ+m1WNhSZutOa/uRblAc9Ut5MQfukFrFMtPSm3bZCA888NmMd5AWXWdgRZ80zd+pTk1P2JrGjg9pUPTvl2PWQ==
-  dependencies:
-    lru-cache "^4.0.1"
-    which "^1.2.9"
-
 cross-spawn@^6.0.0, cross-spawn@^6.0.5:
   version "6.0.5"
   resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
@@ -4038,6 +4276,15 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5:
     shebang-command "^1.2.0"
     which "^1.2.9"
 
+cross-spawn@^7.0.2:
+  version "7.0.3"
+  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
+  integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
+  dependencies:
+    path-key "^3.1.0"
+    shebang-command "^2.0.0"
+    which "^2.0.1"
+
 crypto-browserify@^3.11.0:
   version "3.12.0"
   resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
@@ -4159,6 +4406,11 @@ cyclist@^1.0.1:
   resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
   integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=
 
+damerau-levenshtein@^1.0.8:
+  version "1.0.8"
+  resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7"
+  integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==
+
 dargs@^4.0.1:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/dargs/-/dargs-4.1.0.tgz#03a9dbb4b5c2f139bf14ae53f0b8a2a6a86f4e17"
@@ -4201,7 +4453,7 @@ debug@3.1.0:
   dependencies:
     ms "2.0.0"
 
-debug@^3.1.0, debug@^3.1.1, debug@^3.2.5:
+debug@^3.1.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.7:
   version "3.2.7"
   resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
   integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
@@ -4215,6 +4467,13 @@ debug@^4.1.0:
   dependencies:
     ms "2.1.2"
 
+debug@^4.1.1, debug@^4.3.2, debug@^4.3.4:
+  version "4.3.4"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
+  integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
+  dependencies:
+    ms "2.1.2"
+
 debuglog@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
@@ -4262,6 +4521,11 @@ deep-equal@^1.0.1:
     object-keys "^1.1.1"
     regexp.prototype.flags "^1.2.0"
 
+deep-is@^0.1.3:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
+  integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
+
 deep-is@~0.1.3:
   version "0.1.3"
   resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
@@ -4296,6 +4560,14 @@ define-properties@^1.1.2, define-properties@^1.1.3:
   dependencies:
     object-keys "^1.0.12"
 
+define-properties@^1.1.4:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5"
+  integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==
+  dependencies:
+    has-property-descriptors "^1.0.0"
+    object-keys "^1.1.1"
+
 define-property@^0.2.5:
   version "0.2.5"
   resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116"
@@ -4419,6 +4691,13 @@ dir-glob@^2.0.0, dir-glob@^2.2.2:
   dependencies:
     path-type "^3.0.0"
 
+dir-glob@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
+  integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==
+  dependencies:
+    path-type "^4.0.0"
+
 dns-equal@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d"
@@ -4447,6 +4726,20 @@ doctrine@0.7.2, doctrine@^0.7.2:
     esutils "^1.1.6"
     isarray "0.0.1"
 
+doctrine@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d"
+  integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==
+  dependencies:
+    esutils "^2.0.2"
+
+doctrine@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
+  integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
+  dependencies:
+    esutils "^2.0.2"
+
 dom-converter@^0.2.0:
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768"
@@ -4586,6 +4879,11 @@ emoji-regex@^7.0.1:
   resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
   integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==
 
+emoji-regex@^9.2.2:
+  version "9.2.2"
+  resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72"
+  integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==
+
 emojis-list@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
@@ -4644,7 +4942,7 @@ err-code@^1.0.0:
   resolved "https://registry.yarnpkg.com/err-code/-/err-code-1.1.2.tgz#06e0116d3028f6aef4806849eb0ea6a748ae6960"
   integrity sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=
 
-errno@^0.1.1, errno@^0.1.3, errno@~0.1.7:
+errno@^0.1.3, errno@~0.1.7:
   version "0.1.8"
   resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f"
   integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==
@@ -4680,6 +4978,62 @@ es-abstract@^1.18.0-next.2:
     string.prototype.trimstart "^1.0.4"
     unbox-primitive "^1.0.1"
 
+es-abstract@^1.19.0, es-abstract@^1.20.4:
+  version "1.21.2"
+  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.2.tgz#a56b9695322c8a185dc25975aa3b8ec31d0e7eff"
+  integrity sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==
+  dependencies:
+    array-buffer-byte-length "^1.0.0"
+    available-typed-arrays "^1.0.5"
+    call-bind "^1.0.2"
+    es-set-tostringtag "^2.0.1"
+    es-to-primitive "^1.2.1"
+    function.prototype.name "^1.1.5"
+    get-intrinsic "^1.2.0"
+    get-symbol-description "^1.0.0"
+    globalthis "^1.0.3"
+    gopd "^1.0.1"
+    has "^1.0.3"
+    has-property-descriptors "^1.0.0"
+    has-proto "^1.0.1"
+    has-symbols "^1.0.3"
+    internal-slot "^1.0.5"
+    is-array-buffer "^3.0.2"
+    is-callable "^1.2.7"
+    is-negative-zero "^2.0.2"
+    is-regex "^1.1.4"
+    is-shared-array-buffer "^1.0.2"
+    is-string "^1.0.7"
+    is-typed-array "^1.1.10"
+    is-weakref "^1.0.2"
+    object-inspect "^1.12.3"
+    object-keys "^1.1.1"
+    object.assign "^4.1.4"
+    regexp.prototype.flags "^1.4.3"
+    safe-regex-test "^1.0.0"
+    string.prototype.trim "^1.2.7"
+    string.prototype.trimend "^1.0.6"
+    string.prototype.trimstart "^1.0.6"
+    typed-array-length "^1.0.4"
+    unbox-primitive "^1.0.2"
+    which-typed-array "^1.1.9"
+
+es-set-tostringtag@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8"
+  integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==
+  dependencies:
+    get-intrinsic "^1.1.3"
+    has "^1.0.3"
+    has-tostringtag "^1.0.0"
+
+es-shim-unscopables@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241"
+  integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==
+  dependencies:
+    has "^1.0.3"
+
 es-to-primitive@^1.2.1:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
@@ -4738,6 +5092,111 @@ escodegen@^1.9.1:
   optionalDependencies:
     source-map "~0.6.1"
 
+eslint-config-airbnb-base@^15.0.0:
+  version "15.0.0"
+  resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz#6b09add90ac79c2f8d723a2580e07f3925afd236"
+  integrity sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==
+  dependencies:
+    confusing-browser-globals "^1.0.10"
+    object.assign "^4.1.2"
+    object.entries "^1.1.5"
+    semver "^6.3.0"
+
+eslint-config-airbnb-typescript@17.0.0:
+  version "17.0.0"
+  resolved "https://registry.yarnpkg.com/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-17.0.0.tgz#360dbcf810b26bbcf2ff716198465775f1c49a07"
+  integrity sha512-elNiuzD0kPAPTXjFWg+lE24nMdHMtuxgYoD30OyMD6yrW1AhFZPAg27VX7d3tzOErw+dgJTNWfRSDqEcXb4V0g==
+  dependencies:
+    eslint-config-airbnb-base "^15.0.0"
+
+eslint-config-airbnb@19.0.4:
+  version "19.0.4"
+  resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz#84d4c3490ad70a0ffa571138ebcdea6ab085fdc3"
+  integrity sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==
+  dependencies:
+    eslint-config-airbnb-base "^15.0.0"
+    object.assign "^4.1.2"
+    object.entries "^1.1.5"
+
+eslint-import-resolver-node@^0.3.6:
+  version "0.3.7"
+  resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz#83b375187d412324a1963d84fa664377a23eb4d7"
+  integrity sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==
+  dependencies:
+    debug "^3.2.7"
+    is-core-module "^2.11.0"
+    resolve "^1.22.1"
+
+eslint-module-utils@^2.7.3:
+  version "2.7.4"
+  resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz#4f3e41116aaf13a20792261e61d3a2e7e0583974"
+  integrity sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==
+  dependencies:
+    debug "^3.2.7"
+
+eslint-plugin-import@2.26.0:
+  version "2.26.0"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz#f812dc47be4f2b72b478a021605a59fc6fe8b88b"
+  integrity sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==
+  dependencies:
+    array-includes "^3.1.4"
+    array.prototype.flat "^1.2.5"
+    debug "^2.6.9"
+    doctrine "^2.1.0"
+    eslint-import-resolver-node "^0.3.6"
+    eslint-module-utils "^2.7.3"
+    has "^1.0.3"
+    is-core-module "^2.8.1"
+    is-glob "^4.0.3"
+    minimatch "^3.1.2"
+    object.values "^1.1.5"
+    resolve "^1.22.0"
+    tsconfig-paths "^3.14.1"
+
+eslint-plugin-jsx-a11y@6.6.1:
+  version "6.6.1"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz#93736fc91b83fdc38cc8d115deedfc3091aef1ff"
+  integrity sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q==
+  dependencies:
+    "@babel/runtime" "^7.18.9"
+    aria-query "^4.2.2"
+    array-includes "^3.1.5"
+    ast-types-flow "^0.0.7"
+    axe-core "^4.4.3"
+    axobject-query "^2.2.0"
+    damerau-levenshtein "^1.0.8"
+    emoji-regex "^9.2.2"
+    has "^1.0.3"
+    jsx-ast-utils "^3.3.2"
+    language-tags "^1.0.5"
+    minimatch "^3.1.2"
+    semver "^6.3.0"
+
+eslint-plugin-react-hooks@4.6.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3"
+  integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==
+
+eslint-plugin-react@7.31.10:
+  version "7.31.10"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.31.10.tgz#6782c2c7fe91c09e715d536067644bbb9491419a"
+  integrity sha512-e4N/nc6AAlg4UKW/mXeYWd3R++qUano5/o+t+wnWxIf+bLsOaH3a4q74kX3nDjYym3VBN4HyO9nEn1GcAqgQOA==
+  dependencies:
+    array-includes "^3.1.5"
+    array.prototype.flatmap "^1.3.0"
+    doctrine "^2.1.0"
+    estraverse "^5.3.0"
+    jsx-ast-utils "^2.4.1 || ^3.0.0"
+    minimatch "^3.1.2"
+    object.entries "^1.1.5"
+    object.fromentries "^2.0.5"
+    object.hasown "^1.1.1"
+    object.values "^1.1.5"
+    prop-types "^15.8.1"
+    resolve "^2.0.0-next.3"
+    semver "^6.3.0"
+    string.prototype.matchall "^4.0.7"
+
 eslint-scope@^4.0.0:
   version "4.0.3"
   resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848"
@@ -4746,12 +5205,106 @@ eslint-scope@^4.0.0:
     esrecurse "^4.1.0"
     estraverse "^4.1.1"
 
+eslint-scope@^5.1.1:
+  version "5.1.1"
+  resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
+  integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
+  dependencies:
+    esrecurse "^4.3.0"
+    estraverse "^4.1.1"
+
+eslint-scope@^7.1.1:
+  version "7.1.1"
+  resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642"
+  integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==
+  dependencies:
+    esrecurse "^4.3.0"
+    estraverse "^5.2.0"
+
+eslint-utils@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672"
+  integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==
+  dependencies:
+    eslint-visitor-keys "^2.0.0"
+
+eslint-visitor-keys@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303"
+  integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==
+
+eslint-visitor-keys@^3.3.0:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826"
+  integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
+
+eslint@8.26.0:
+  version "8.26.0"
+  resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.26.0.tgz#2bcc8836e6c424c4ac26a5674a70d44d84f2181d"
+  integrity sha512-kzJkpaw1Bfwheq4VXUezFriD1GxszX6dUekM7Z3aC2o4hju+tsR/XyTC3RcoSD7jmy9VkPU3+N6YjVU2e96Oyg==
+  dependencies:
+    "@eslint/eslintrc" "^1.3.3"
+    "@humanwhocodes/config-array" "^0.11.6"
+    "@humanwhocodes/module-importer" "^1.0.1"
+    "@nodelib/fs.walk" "^1.2.8"
+    ajv "^6.10.0"
+    chalk "^4.0.0"
+    cross-spawn "^7.0.2"
+    debug "^4.3.2"
+    doctrine "^3.0.0"
+    escape-string-regexp "^4.0.0"
+    eslint-scope "^7.1.1"
+    eslint-utils "^3.0.0"
+    eslint-visitor-keys "^3.3.0"
+    espree "^9.4.0"
+    esquery "^1.4.0"
+    esutils "^2.0.2"
+    fast-deep-equal "^3.1.3"
+    file-entry-cache "^6.0.1"
+    find-up "^5.0.0"
+    glob-parent "^6.0.2"
+    globals "^13.15.0"
+    grapheme-splitter "^1.0.4"
+    ignore "^5.2.0"
+    import-fresh "^3.0.0"
+    imurmurhash "^0.1.4"
+    is-glob "^4.0.0"
+    is-path-inside "^3.0.3"
+    js-sdsl "^4.1.4"
+    js-yaml "^4.1.0"
+    json-stable-stringify-without-jsonify "^1.0.1"
+    levn "^0.4.1"
+    lodash.merge "^4.6.2"
+    minimatch "^3.1.2"
+    natural-compare "^1.4.0"
+    optionator "^0.9.1"
+    regexpp "^3.2.0"
+    strip-ansi "^6.0.1"
+    strip-json-comments "^3.1.0"
+    text-table "^0.2.0"
+
+espree@^9.4.0:
+  version "9.5.0"
+  resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.0.tgz#3646d4e3f58907464edba852fa047e6a27bdf113"
+  integrity sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==
+  dependencies:
+    acorn "^8.8.0"
+    acorn-jsx "^5.3.2"
+    eslint-visitor-keys "^3.3.0"
+
 esprima@^4.0.0, esprima@^4.0.1:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
   integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
 
-esrecurse@^4.1.0:
+esquery@^1.4.0:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b"
+  integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==
+  dependencies:
+    estraverse "^5.1.0"
+
+esrecurse@^4.1.0, esrecurse@^4.3.0:
   version "4.3.0"
   resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
   integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==
@@ -4763,6 +5316,11 @@ estraverse@^4.1.1, estraverse@^4.2.0:
   resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
   integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
 
+estraverse@^5.1.0, estraverse@^5.3.0:
+  version "5.3.0"
+  resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
+  integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
+
 estraverse@^5.2.0:
   version "5.2.0"
   resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880"
@@ -5003,7 +5561,7 @@ extsprintf@^1.2.0:
   resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
   integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
 
-fast-deep-equal@^3.1.1:
+fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
   version "3.1.3"
   resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
   integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
@@ -5020,21 +5578,39 @@ fast-glob@^2.2.6:
     merge2 "^1.2.3"
     micromatch "^3.1.10"
 
+fast-glob@^3.2.9:
+  version "3.2.12"
+  resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80"
+  integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==
+  dependencies:
+    "@nodelib/fs.stat" "^2.0.2"
+    "@nodelib/fs.walk" "^1.2.3"
+    glob-parent "^5.1.2"
+    merge2 "^1.3.0"
+    micromatch "^4.0.4"
+
 fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
   integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
 
-fast-levenshtein@~2.0.6:
+fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6:
   version "2.0.6"
   resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
-  integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
+  integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
 
 fastparse@^1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9"
   integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==
 
+fastq@^1.6.0:
+  version "1.15.0"
+  resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a"
+  integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==
+  dependencies:
+    reusify "^1.0.4"
+
 faye-websocket@^0.10.0:
   version "0.10.0"
   resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4"
@@ -5068,6 +5644,13 @@ figures@^2.0.0:
   dependencies:
     escape-string-regexp "^1.0.5"
 
+file-entry-cache@^6.0.1:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
+  integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==
+  dependencies:
+    flat-cache "^3.0.4"
+
 file-loader@2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-2.0.0.tgz#39749c82f020b9e85901dcff98e8004e6401cfde"
@@ -5193,6 +5776,14 @@ find-up@^4.1.0:
     locate-path "^5.0.0"
     path-exists "^4.0.0"
 
+find-up@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
+  integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
+  dependencies:
+    locate-path "^6.0.0"
+    path-exists "^4.0.0"
+
 findup-sync@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-2.0.0.tgz#9326b1488c22d1a6088650a86901b2d9a90a2cbc"
@@ -5203,6 +5794,19 @@ findup-sync@^2.0.0:
     micromatch "^3.0.4"
     resolve-dir "^1.0.1"
 
+flat-cache@^3.0.4:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11"
+  integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==
+  dependencies:
+    flatted "^3.1.0"
+    rimraf "^3.0.2"
+
+flatted@^3.1.0:
+  version "3.2.7"
+  resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787"
+  integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==
+
 flush-write-stream@^1.0.0:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8"
@@ -5223,11 +5827,6 @@ for-each@^0.3.3:
   dependencies:
     is-callable "^1.1.3"
 
-for-in@^0.1.3:
-  version "0.1.8"
-  resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1"
-  integrity sha512-F0to7vbBSHP8E3l6dCjxNOLuSFAACIxFy3UehTUlG7svlXi37HHsDkyVcHo0Pq8QwrE+pXvWSVX3ZT1T9wAZ9g==
-
 for-in@^1.0.1, for-in@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
@@ -5240,13 +5839,6 @@ for-own@^0.1.4:
   dependencies:
     for-in "^1.0.1"
 
-for-own@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/for-own/-/for-own-1.0.0.tgz#c63332f415cedc4b04dbfe70cf836494c53cb44b"
-  integrity sha512-0OABksIGrxKK8K4kynWkQ7y1zounQxP+CWnyclVwj81KW3vlLlGUx57DKGcP/LH216GzqnstnPocF16Nxs0Ycg==
-  dependencies:
-    for-in "^1.0.1"
-
 forever-agent@~0.6.1:
   version "0.6.1"
   resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
@@ -5330,21 +5922,26 @@ fsevents@~2.3.2:
   resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
   integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
 
-fstream@^1.0.0, fstream@^1.0.12:
-  version "1.0.12"
-  resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045"
-  integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==
-  dependencies:
-    graceful-fs "^4.1.2"
-    inherits "~2.0.0"
-    mkdirp ">=0.5 0"
-    rimraf "2"
-
 function-bind@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
   integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
 
+function.prototype.name@^1.1.5:
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621"
+  integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.3"
+    es-abstract "^1.19.0"
+    functions-have-names "^1.2.2"
+
+functions-have-names@^1.2.2:
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
+  integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
+
 gauge@~2.7.3:
   version "2.7.4"
   resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
@@ -5359,13 +5956,6 @@ gauge@~2.7.3:
     strip-ansi "^3.0.1"
     wide-align "^1.1.0"
 
-gaze@^1.0.0:
-  version "1.1.3"
-  resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a"
-  integrity sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==
-  dependencies:
-    globule "^1.0.0"
-
 genfun@^5.0.0:
   version "5.0.0"
   resolved "https://registry.yarnpkg.com/genfun/-/genfun-5.0.0.tgz#9dd9710a06900a5c4a5bf57aca5da4e52fe76537"
@@ -5390,6 +5980,15 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1:
     has "^1.0.3"
     has-symbols "^1.0.1"
 
+get-intrinsic@^1.1.3, get-intrinsic@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f"
+  integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==
+  dependencies:
+    function-bind "^1.1.1"
+    has "^1.0.3"
+    has-symbols "^1.0.3"
+
 get-pkg-repo@^1.0.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/get-pkg-repo/-/get-pkg-repo-1.4.0.tgz#c73b489c06d80cc5536c2c853f9e05232056972d"
@@ -5423,6 +6022,14 @@ get-stream@^4.0.0, get-stream@^4.1.0:
   dependencies:
     pump "^3.0.0"
 
+get-symbol-description@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6"
+  integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==
+  dependencies:
+    call-bind "^1.0.2"
+    get-intrinsic "^1.1.1"
+
 get-value@^2.0.3, get-value@^2.0.6:
   version "2.0.6"
   resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
@@ -5512,13 +6119,20 @@ glob-parent@^3.1.0:
     is-glob "^3.1.0"
     path-dirname "^1.0.0"
 
-glob-parent@^5.0.0, glob-parent@~5.1.2:
+glob-parent@^5.0.0, glob-parent@^5.1.2, glob-parent@~5.1.2:
   version "5.1.2"
   resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
   integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
   dependencies:
     is-glob "^4.0.1"
 
+glob-parent@^6.0.2:
+  version "6.0.2"
+  resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3"
+  integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==
+  dependencies:
+    is-glob "^4.0.3"
+
 glob-to-regexp@0.4.1:
   version "0.4.1"
   resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
@@ -5529,19 +6143,7 @@ glob-to-regexp@^0.3.0:
   resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
   integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=
 
-glob@^7.0.0:
-  version "7.2.3"
-  resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
-  integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
-  dependencies:
-    fs.realpath "^1.0.0"
-    inflight "^1.0.4"
-    inherits "2"
-    minimatch "^3.1.1"
-    once "^1.3.0"
-    path-is-absolute "^1.0.0"
-
-glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@~7.1.1:
+glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4:
   version "7.1.7"
   resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90"
   integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==
@@ -5583,11 +6185,37 @@ globals@^11.1.0:
   resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
   integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
 
+globals@^13.15.0, globals@^13.19.0:
+  version "13.20.0"
+  resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82"
+  integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==
+  dependencies:
+    type-fest "^0.20.2"
+
 globals@^9.18.0:
   version "9.18.0"
   resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
   integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==
 
+globalthis@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf"
+  integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==
+  dependencies:
+    define-properties "^1.1.3"
+
+globby@^11.1.0:
+  version "11.1.0"
+  resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b"
+  integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==
+  dependencies:
+    array-union "^2.1.0"
+    dir-glob "^3.0.1"
+    fast-glob "^3.2.9"
+    ignore "^5.2.0"
+    merge2 "^1.4.1"
+    slash "^3.0.0"
+
 globby@^6.1.0:
   version "6.1.0"
   resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c"
@@ -5625,14 +6253,12 @@ globby@^9.2.0:
     pify "^4.0.1"
     slash "^2.0.0"
 
-globule@^1.0.0:
-  version "1.3.4"
-  resolved "https://registry.yarnpkg.com/globule/-/globule-1.3.4.tgz#7c11c43056055a75a6e68294453c17f2796170fb"
-  integrity sha512-OPTIfhMBh7JbBYDpa5b+Q5ptmMWKwcNcFSR/0c6t8V4f3ZAVBEsKNY37QdVqmLRYSMhOUGYrY0QhSoEpzGr/Eg==
+gopd@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c"
+  integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==
   dependencies:
-    glob "~7.1.1"
-    lodash "^4.17.21"
-    minimatch "~3.0.2"
+    get-intrinsic "^1.1.3"
 
 graceful-fs@4.1.4:
   version "4.1.4"
@@ -5644,6 +6270,11 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6
   resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
   integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
 
+grapheme-splitter@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e"
+  integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==
+
 growly@^1.3.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
@@ -5696,6 +6327,11 @@ has-bigints@^1.0.1:
   resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113"
   integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==
 
+has-bigints@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa"
+  integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==
+
 has-flag@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
@@ -5706,11 +6342,40 @@ has-flag@^3.0.0:
   resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
   integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
 
-has-symbols@^1.0.1, has-symbols@^1.0.2:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423"
+has-flag@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+  integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
+has-property-descriptors@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861"
+  integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==
+  dependencies:
+    get-intrinsic "^1.1.1"
+
+has-proto@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0"
+  integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==
+
+has-symbols@^1.0.1, has-symbols@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423"
   integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==
 
+has-symbols@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
+  integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
+
+has-tostringtag@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25"
+  integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==
+  dependencies:
+    has-symbols "^1.0.2"
+
 has-unicode@^2.0.0, has-unicode@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
@@ -6078,10 +6743,10 @@ ignore@^4.0.3:
   resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
   integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
 
-image-size@~0.5.0:
-  version "0.5.5"
-  resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c"
-  integrity sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==
+ignore@^5.2.0:
+  version "5.2.4"
+  resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324"
+  integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==
 
 import-cwd@^2.0.0:
   version "2.1.0"
@@ -6098,7 +6763,7 @@ import-fresh@^2.0.0:
     caller-path "^2.0.0"
     resolve-from "^3.0.0"
 
-import-fresh@^3.1.0:
+import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1:
   version "3.3.0"
   resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
   integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
@@ -6134,11 +6799,6 @@ imurmurhash@^0.1.4:
   resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
   integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
 
-in-publish@^2.0.0:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.1.tgz#948b1a535c8030561cea522f73f78f4be357e00c"
-  integrity sha512-oDM0kUSNFC31ShNxHKUyfZKy8ZeXZBWMjMdZHKLOk13uvT27VTL/QzRGfRUcevJhpkZAvlhPYuXkF7eNWrtyxQ==
-
 indent-string@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80"
@@ -6169,7 +6829,7 @@ inflight@^1.0.4:
     once "^1.3.0"
     wrappy "1"
 
-inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3:
+inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3:
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
   integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@@ -6230,6 +6890,15 @@ internal-ip@^3.0.1:
     default-gateway "^2.6.0"
     ipaddr.js "^1.5.2"
 
+internal-slot@^1.0.3, internal-slot@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986"
+  integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==
+  dependencies:
+    get-intrinsic "^1.2.0"
+    has "^1.0.3"
+    side-channel "^1.0.4"
+
 interpret@^1.1.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e"
@@ -6247,11 +6916,6 @@ inversify@^5.0.0:
   resolved "https://registry.yarnpkg.com/inversify/-/inversify-5.1.1.tgz#6fbd668c591337404e005a1946bfe0d802c08730"
   integrity sha512-j8grHGDzv1v+8T1sAQ+3boTCntFPfvxLCkNcxB1J8qA0lUN+fAlSyYd+RXKvaPRL4AGyPxViutBEJHNXOyUdFQ==
 
-invert-kv@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
-  integrity sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==
-
 invert-kv@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02"
@@ -6293,6 +6957,15 @@ is-arguments@^1.0.4:
   dependencies:
     call-bind "^1.0.0"
 
+is-array-buffer@^3.0.1, is-array-buffer@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe"
+  integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==
+  dependencies:
+    call-bind "^1.0.2"
+    get-intrinsic "^1.2.0"
+    is-typed-array "^1.1.10"
+
 is-arrayish@^0.2.1:
   version "0.2.1"
   resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
@@ -6334,6 +7007,11 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.3:
   resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e"
   integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==
 
+is-callable@^1.2.7:
+  version "1.2.7"
+  resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055"
+  integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==
+
 is-ci@^1.0.10:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.2.1.tgz#e3779c8ee17fccf428488f6e281187f2e632841c"
@@ -6348,6 +7026,13 @@ is-ci@^2.0.0:
   dependencies:
     ci-info "^2.0.0"
 
+is-core-module@^2.11.0, is-core-module@^2.8.1, is-core-module@^2.9.0:
+  version "2.11.0"
+  resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144"
+  integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==
+  dependencies:
+    has "^1.0.3"
+
 is-core-module@^2.2.0:
   version "2.4.0"
   resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1"
@@ -6474,6 +7159,13 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1:
   dependencies:
     is-extglob "^2.1.1"
 
+is-glob@^4.0.3:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
+  integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
+  dependencies:
+    is-extglob "^2.1.1"
+
 is-in-browser@^1.0.2, is-in-browser@^1.1.3:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835"
@@ -6484,6 +7176,11 @@ is-negative-zero@^2.0.1:
   resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24"
   integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==
 
+is-negative-zero@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150"
+  integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==
+
 is-number-object@^1.0.4:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.5.tgz#6edfaeed7950cff19afedce9fbfca9ee6dd289eb"
@@ -6542,6 +7239,11 @@ is-path-inside@^1.0.0:
   dependencies:
     path-is-inside "^1.0.1"
 
+is-path-inside@^3.0.3:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
+  integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
+
 is-plain-obj@^1.0.0, is-plain-obj@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
@@ -6577,6 +7279,21 @@ is-regex@^1.0.4, is-regex@^1.1.3:
     call-bind "^1.0.2"
     has-symbols "^1.0.2"
 
+is-regex@^1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958"
+  integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==
+  dependencies:
+    call-bind "^1.0.2"
+    has-tostringtag "^1.0.0"
+
+is-shared-array-buffer@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79"
+  integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==
+  dependencies:
+    call-bind "^1.0.2"
+
 is-ssh@^1.3.0:
   version "1.3.3"
   resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.3.tgz#7f133285ccd7f2c2c7fc897b771b53d95a2b2c7e"
@@ -6594,6 +7311,13 @@ is-string@^1.0.5, is-string@^1.0.6:
   resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.6.tgz#3fe5d5992fb0d93404f32584d4b0179a71b54a5f"
   integrity sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==
 
+is-string@^1.0.7:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd"
+  integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==
+  dependencies:
+    has-tostringtag "^1.0.0"
+
 is-symbol@^1.0.2, is-symbol@^1.0.3:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c"
@@ -6608,6 +7332,17 @@ is-text-path@^1.0.1:
   dependencies:
     text-extensions "^1.0.0"
 
+is-typed-array@^1.1.10, is-typed-array@^1.1.9:
+  version "1.1.10"
+  resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f"
+  integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==
+  dependencies:
+    available-typed-arrays "^1.0.5"
+    call-bind "^1.0.2"
+    for-each "^0.3.3"
+    gopd "^1.0.1"
+    has-tostringtag "^1.0.0"
+
 is-typedarray@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
@@ -6618,6 +7353,13 @@ is-utf8@^0.2.0:
   resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
   integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=
 
+is-weakref@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2"
+  integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==
+  dependencies:
+    call-bind "^1.0.2"
+
 is-windows@^1.0.0, is-windows@^1.0.1, is-windows@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
@@ -7052,16 +7794,16 @@ jquery@3.3.1:
   resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca"
   integrity sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg==
 
-js-base64@^2.1.8:
-  version "2.6.4"
-  resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.6.4.tgz#f4e686c5de1ea1f867dbcad3d46d969428df98c4"
-  integrity sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==
-
 js-levenshtein@^1.1.3:
   version "1.1.6"
   resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d"
   integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==
 
+js-sdsl@^4.1.4:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.3.0.tgz#aeefe32a451f7af88425b11fdb5f58c90ae1d711"
+  integrity sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==
+
 "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
@@ -7080,6 +7822,13 @@ js-yaml@^3.13.1, js-yaml@^3.7.0:
     argparse "^1.0.7"
     esprima "^4.0.0"
 
+js-yaml@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
+  integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
+  dependencies:
+    argparse "^2.0.1"
+
 jsbn@~0.1.0:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
@@ -7152,6 +7901,11 @@ json-schema@0.2.3:
   resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
   integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
 
+json-stable-stringify-without-jsonify@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
+  integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
+
 json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1:
   version "5.0.1"
   resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
@@ -7181,6 +7935,13 @@ json5@^1.0.1:
   dependencies:
     minimist "^1.2.0"
 
+json5@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593"
+  integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==
+  dependencies:
+    minimist "^1.2.0"
+
 jsonfile@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
@@ -7298,6 +8059,14 @@ jss@10.8.2, jss@^10.8.2:
     is-in-browser "^1.1.3"
     tiny-warning "^1.0.2"
 
+"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.2:
+  version "3.3.3"
+  resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz#76b3e6e6cece5c69d49a5792c3d01bd1a0cdc7ea"
+  integrity sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==
+  dependencies:
+    array-includes "^3.1.5"
+    object.assign "^4.1.3"
+
 jwa@^1.4.1:
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a"
@@ -7349,12 +8118,17 @@ kleur@^2.0.1:
   resolved "https://registry.yarnpkg.com/kleur/-/kleur-2.0.2.tgz#b704f4944d95e255d038f0cb05fb8a602c55a300"
   integrity sha512-77XF9iTllATmG9lSlIv0qdQ2BQ/h9t0bJllHlbvsQ0zUWfU7Yi0S8L5JXzPZgkefIiajLmBJJ4BsMJmqcf7oxQ==
 
-lcid@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835"
-  integrity sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==
+language-subtag-registry@^0.3.20:
+  version "0.3.22"
+  resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz#2e1500861b2e457eba7e7ae86877cbd08fa1fd1d"
+  integrity sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==
+
+language-tags@^1.0.5:
+  version "1.0.8"
+  resolved "https://registry.yarnpkg.com/language-tags/-/language-tags-1.0.8.tgz#042b4bdb0d4e771a9f8cc2fdc9bb26a52a367312"
+  integrity sha512-aWAZwgPLS8hJ20lNPm9HNVs4inexz6S2sQa3wx/+ycuutMNE5/IfYxiWYBbi+9UWCQVaXYCOPUl6gFrPR7+jGg==
   dependencies:
-    invert-kv "^1.0.0"
+    language-subtag-registry "^0.3.20"
 
 lcid@^2.0.0:
   version "2.0.0"
@@ -7392,36 +8166,19 @@ lerna@3.22.1:
     import-local "^2.0.0"
     npmlog "^4.1.2"
 
-less-loader@4.1.0:
-  version "4.1.0"
-  resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-4.1.0.tgz#2c1352c5b09a4f84101490274fd51674de41363e"
-  integrity sha512-KNTsgCE9tMOM70+ddxp9yyt9iHqgmSs0yTZc5XH5Wo+g80RWRIYNqE58QJKm/yMud5wZEvz50ugRDuzVIkyahg==
-  dependencies:
-    clone "^2.1.1"
-    loader-utils "^1.1.0"
-    pify "^3.0.0"
-
-less@3.8.1:
-  version "3.8.1"
-  resolved "https://registry.yarnpkg.com/less/-/less-3.8.1.tgz#f31758598ef5a1930dd4caefa9e4340641e71e1d"
-  integrity sha512-8HFGuWmL3FhQR0aH89escFNBQH/nEiYPP2ltDFdQw2chE28Yx2E3lhAIq9Y2saYwLSwa699s4dBVEfCY8Drf7Q==
-  dependencies:
-    clone "^2.1.2"
-  optionalDependencies:
-    errno "^0.1.1"
-    graceful-fs "^4.1.2"
-    image-size "~0.5.0"
-    mime "^1.4.1"
-    mkdirp "^0.5.0"
-    promise "^7.1.1"
-    request "^2.83.0"
-    source-map "~0.6.0"
-
 leven@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580"
   integrity sha1-wuep93IJTe6dNCAq6KzORoeHVYA=
 
+levn@^0.4.1:
+  version "0.4.1"
+  resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
+  integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==
+  dependencies:
+    prelude-ls "^1.2.1"
+    type-check "~0.4.0"
+
 levn@~0.3.0:
   version "0.3.0"
   resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
@@ -7497,15 +8254,6 @@ loader-utils@^0.2.16:
     json5 "^0.5.0"
     object-assign "^4.0.1"
 
-loader-utils@^1.0.1:
-  version "1.4.2"
-  resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.2.tgz#29a957f3a63973883eb684f10ffd3d151fec01a3"
-  integrity sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==
-  dependencies:
-    big.js "^5.2.2"
-    emojis-list "^3.0.0"
-    json5 "^1.0.1"
-
 loader-utils@^1.0.2, loader-utils@^1.1.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613"
@@ -7538,6 +8286,13 @@ locate-path@^5.0.0:
   dependencies:
     p-locate "^4.1.0"
 
+locate-path@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
+  integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==
+  dependencies:
+    p-locate "^5.0.0"
+
 lodash._reinterpolate@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
@@ -7593,6 +8348,11 @@ lodash.isstring@^4.0.1:
   resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
   integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=
 
+lodash.merge@^4.6.2:
+  version "4.6.2"
+  resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
+  integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
+
 lodash.once@^4.0.0:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
@@ -7608,11 +8368,6 @@ lodash.sortby@^4.7.0:
   resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
   integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
 
-lodash.tail@^4.1.1:
-  version "4.1.1"
-  resolved "https://registry.yarnpkg.com/lodash.tail/-/lodash.tail-4.1.1.tgz#d2333a36d9e7717c8ad2f7cacafec7c32b444664"
-  integrity sha512-+7y6zfkH4TqgS5DYKIqJuxmL5xT3WUUumVMZVRpDUo0UqJREwZqKmGo9wluj12FbPGl1UjRf2TnAImbw/bKtdw==
-
 lodash.template@^4.0.2, lodash.template@^4.5.0:
   version "4.5.0"
   resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab"
@@ -7633,7 +8388,7 @@ lodash.uniq@^4.5.0:
   resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
   integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
 
-lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1:
+lodash@^4.17.10, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1:
   version "4.17.21"
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
   integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@@ -7663,7 +8418,7 @@ lower-case@^1.1.1:
   resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
   integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw=
 
-lru-cache@^4.0.1, lru-cache@^4.1.1:
+lru-cache@^4.1.1:
   version "4.1.5"
   resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
   integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==
@@ -7822,7 +8577,7 @@ memory-fs@~0.4.1:
     errno "^0.1.3"
     readable-stream "^2.0.1"
 
-meow@^3.3.0, meow@^3.7.0:
+meow@^3.3.0:
   version "3.7.0"
   resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb"
   integrity sha512-TNdwZs0skRlpPpCUK25StC4VH+tP5GgeY1HQOOGP+lQ2xtdkN2VtT/5tiX9k3IWpkBPV9b3LsAWXn4GGi/PrSA==
@@ -7882,7 +8637,7 @@ merge-stream@^1.0.1:
   dependencies:
     readable-stream "^2.0.1"
 
-merge2@^1.2.3:
+merge2@^1.2.3, merge2@^1.3.0, merge2@^1.4.1:
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
   integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
@@ -7935,6 +8690,14 @@ micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.8, mic
     snapdragon "^0.8.1"
     to-regex "^3.0.2"
 
+micromatch@^4.0.4:
+  version "4.0.5"
+  resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
+  integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
+  dependencies:
+    braces "^3.0.2"
+    picomatch "^2.3.1"
+
 miller-rabin@^4.0.0:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d"
@@ -7955,7 +8718,7 @@ mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24:
   dependencies:
     mime-db "1.48.0"
 
-mime@1.6.0, mime@^1.4.1, mime@^1.6.0:
+mime@1.6.0, mime@^1.6.0:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
   integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
@@ -8005,20 +8768,13 @@ minimatch@^3.0.3, minimatch@^3.0.4:
   dependencies:
     brace-expansion "^1.1.7"
 
-minimatch@^3.1.1:
+minimatch@^3.0.5, minimatch@^3.1.2:
   version "3.1.2"
   resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
   integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
   dependencies:
     brace-expansion "^1.1.7"
 
-minimatch@~3.0.2:
-  version "3.0.8"
-  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.8.tgz#5e6a59bd11e2ab0de1cfb843eb2d82e546c321c1"
-  integrity sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==
-  dependencies:
-    brace-expansion "^1.1.7"
-
 minimist-options@4.1.0:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619"
@@ -8106,14 +8862,6 @@ mixin-deep@^1.2.0:
     for-in "^1.0.2"
     is-extendable "^1.0.1"
 
-mixin-object@^2.0.1:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/mixin-object/-/mixin-object-2.0.1.tgz#4fb949441dab182540f1fe035ba60e1947a5e57e"
-  integrity sha512-ALGF1Jt9ouehcaXaHhn6t1yGWRqGaHkPFndtFVHfZXOvkIZ/yoGaSi0AHVTafb3ZBGg4dr/bDwnaEKqCXzchMA==
-  dependencies:
-    for-in "^0.1.3"
-    is-extendable "^0.1.1"
-
 mkdirp-promise@^5.0.1:
   version "5.0.1"
   resolved "https://registry.yarnpkg.com/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz#e9b8f68e552c68a9c1713b84883f7a1dd039b8a1"
@@ -8133,13 +8881,6 @@ mkdirp@0.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.5, mkdirp@~0.5.0:
   dependencies:
     minimist "^1.2.5"
 
-"mkdirp@>=0.5 0":
-  version "0.5.6"
-  resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6"
-  integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==
-  dependencies:
-    minimist "^1.2.6"
-
 modify-values@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022"
@@ -8229,11 +8970,6 @@ nan@^2.12.1:
   resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19"
   integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==
 
-nan@^2.13.2:
-  version "2.17.0"
-  resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb"
-  integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==
-
 nanomatch@^1.2.9:
   version "1.2.13"
   resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
@@ -8251,6 +8987,11 @@ nanomatch@^1.2.9:
     snapdragon "^0.8.1"
     to-regex "^3.0.1"
 
+natural-compare-lite@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4"
+  integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==
+
 natural-compare@^1.4.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
@@ -8297,24 +9038,6 @@ node-forge@^0.10.0:
   resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3"
   integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==
 
-node-gyp@^3.8.0:
-  version "3.8.0"
-  resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.8.0.tgz#540304261c330e80d0d5edce253a68cb3964218c"
-  integrity sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==
-  dependencies:
-    fstream "^1.0.0"
-    glob "^7.0.3"
-    graceful-fs "^4.1.2"
-    mkdirp "^0.5.0"
-    nopt "2 || 3"
-    npmlog "0 || 1 || 2 || 3 || 4"
-    osenv "0"
-    request "^2.87.0"
-    rimraf "2"
-    semver "~5.3.0"
-    tar "^2.0.0"
-    which "1"
-
 node-gyp@^5.0.2:
   version "5.1.1"
   resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-5.1.1.tgz#eb915f7b631c937d282e33aed44cb7a025f62a3e"
@@ -8382,36 +9105,6 @@ node-releases@^1.1.71:
   resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.73.tgz#dd4e81ddd5277ff846b80b52bb40c49edf7a7b20"
   integrity sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==
 
-node-sass@4.12.0:
-  version "4.12.0"
-  resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.12.0.tgz#0914f531932380114a30cc5fa4fa63233a25f017"
-  integrity sha512-A1Iv4oN+Iel6EPv77/HddXErL2a+gZ4uBeZUy+a8O35CFYTXhgA8MgLCWBtwpGZdCvTvQ9d+bQxX/QC36GDPpQ==
-  dependencies:
-    async-foreach "^0.1.3"
-    chalk "^1.1.1"
-    cross-spawn "^3.0.0"
-    gaze "^1.0.0"
-    get-stdin "^4.0.1"
-    glob "^7.0.3"
-    in-publish "^2.0.0"
-    lodash "^4.17.11"
-    meow "^3.7.0"
-    mkdirp "^0.5.1"
-    nan "^2.13.2"
-    node-gyp "^3.8.0"
-    npmlog "^4.0.0"
-    request "^2.88.0"
-    sass-graph "^2.2.4"
-    stdout-stream "^1.4.0"
-    "true-case-path" "^1.0.2"
-
-"nopt@2 || 3":
-  version "3.0.6"
-  resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9"
-  integrity sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==
-  dependencies:
-    abbrev "1"
-
 nopt@^4.0.1:
   version "4.0.3"
   resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48"
@@ -8531,7 +9224,7 @@ npm-run-path@^2.0.0:
   dependencies:
     path-key "^2.0.0"
 
-"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.1.2:
+npmlog@^4.1.2:
   version "4.1.2"
   resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
   integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
@@ -8587,6 +9280,11 @@ object-inspect@^1.10.3, object-inspect@^1.9.0:
   resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.3.tgz#c2aa7d2d09f50c99375704f7a0adf24c5782d369"
   integrity sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==
 
+object-inspect@^1.12.3:
+  version "1.12.3"
+  resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9"
+  integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==
+
 object-is@^1.0.1:
   version "1.1.5"
   resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac"
@@ -8617,6 +9315,34 @@ object.assign@^4.1.0, object.assign@^4.1.2:
     has-symbols "^1.0.1"
     object-keys "^1.1.1"
 
+object.assign@^4.1.3, object.assign@^4.1.4:
+  version "4.1.4"
+  resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f"
+  integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.4"
+    has-symbols "^1.0.3"
+    object-keys "^1.1.1"
+
+object.entries@^1.1.5:
+  version "1.1.6"
+  resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.6.tgz#9737d0e5b8291edd340a3e3264bb8a3b00d5fa23"
+  integrity sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.4"
+    es-abstract "^1.20.4"
+
+object.fromentries@^2.0.5:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.6.tgz#cdb04da08c539cffa912dcd368b886e0904bfa73"
+  integrity sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.4"
+    es-abstract "^1.20.4"
+
 object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.1:
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz#1bd63aeacf0d5d2d2f31b5e393b03a7c601a23f7"
@@ -8626,6 +9352,14 @@ object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.1
     define-properties "^1.1.3"
     es-abstract "^1.18.0-next.2"
 
+object.hasown@^1.1.1:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.2.tgz#f919e21fad4eb38a57bc6345b3afd496515c3f92"
+  integrity sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==
+  dependencies:
+    define-properties "^1.1.4"
+    es-abstract "^1.20.4"
+
 object.omit@^2.0.0:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa"
@@ -8641,6 +9375,15 @@ object.pick@^1.3.0:
   dependencies:
     isobject "^3.0.1"
 
+object.values@^1.1.5:
+  version "1.1.6"
+  resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d"
+  integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.4"
+    es-abstract "^1.20.4"
+
 obuf@^1.0.0, obuf@^1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e"
@@ -8709,6 +9452,18 @@ optionator@^0.8.1:
     type-check "~0.3.2"
     word-wrap "~1.2.3"
 
+optionator@^0.9.1:
+  version "0.9.1"
+  resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499"
+  integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==
+  dependencies:
+    deep-is "^0.1.3"
+    fast-levenshtein "^2.0.6"
+    levn "^0.4.1"
+    prelude-ls "^1.2.1"
+    type-check "^0.4.0"
+    word-wrap "^1.2.3"
+
 original@^1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f"
@@ -8726,13 +9481,6 @@ os-homedir@^1.0.0:
   resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
   integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M=
 
-os-locale@^1.4.0:
-  version "1.4.0"
-  resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9"
-  integrity sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g==
-  dependencies:
-    lcid "^1.0.0"
-
 os-locale@^3.0.0, os-locale@^3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a"
@@ -8755,7 +9503,7 @@ os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2:
   resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
   integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
 
-osenv@0, osenv@^0.1.4, osenv@^0.1.5:
+osenv@^0.1.4, osenv@^0.1.5:
   version "0.1.5"
   resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410"
   integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==
@@ -8792,6 +9540,13 @@ p-limit@^2.0.0, p-limit@^2.2.0:
   dependencies:
     p-try "^2.0.0"
 
+p-limit@^3.0.2:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
+  integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
+  dependencies:
+    yocto-queue "^0.1.0"
+
 p-locate@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
@@ -8813,6 +9568,13 @@ p-locate@^4.1.0:
   dependencies:
     p-limit "^2.2.0"
 
+p-locate@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
+  integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==
+  dependencies:
+    p-limit "^3.0.2"
+
 p-map-series@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-1.0.0.tgz#bf98fe575705658a9e1351befb85ae4c1f07bdca"
@@ -9025,7 +9787,12 @@ path-key@^2.0.0, path-key@^2.0.1:
   resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
   integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
 
-path-parse@^1.0.5, path-parse@^1.0.6:
+path-key@^3.1.0:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
+  integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
+
+path-parse@^1.0.5, path-parse@^1.0.6, path-parse@^1.0.7:
   version "1.0.7"
   resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
   integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
@@ -9084,6 +9851,11 @@ picomatch@^2.0.4, picomatch@^2.2.1:
   resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972"
   integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==
 
+picomatch@^2.3.1:
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
+  integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
+
 pify@^2.0.0, pify@^2.3.0:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
@@ -9216,6 +9988,11 @@ postcss@^7.0.0, postcss@^7.0.2:
     source-map "^0.6.1"
     supports-color "^6.1.0"
 
+prelude-ls@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
+  integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
+
 prelude-ls@~1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
@@ -9270,13 +10047,6 @@ promise-retry@^1.1.1:
     err-code "^1.0.0"
     retry "^0.10.0"
 
-promise@^7.1.1:
-  version "7.3.1"
-  resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
-  integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==
-  dependencies:
-    asap "~2.0.3"
-
 prompts@^0.1.9:
   version "0.1.14"
   resolved "https://registry.yarnpkg.com/prompts/-/prompts-0.1.14.tgz#a8e15c612c5c9ec8f8111847df3337c9cbd443b2"
@@ -9301,7 +10071,7 @@ prop-types@15.7.2, prop-types@^15.5.8, prop-types@^15.6.2, prop-types@^15.7.2:
     object-assign "^4.1.1"
     react-is "^16.8.1"
 
-prop-types@^15.8.1:
+prop-types@^15.5.4, prop-types@^15.8.1:
   version "15.8.1"
   resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
   integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@@ -9454,6 +10224,11 @@ querystringify@^2.1.1:
   resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
   integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==
 
+queue-microtask@^1.2.2:
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
+  integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
+
 quick-lru@^1.0.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8"
@@ -9542,6 +10317,11 @@ react-is@^18.2.0:
   resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
   integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
 
+react-lifecycles-compat@^3.0.4:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
+  integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
+
 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"
@@ -9571,6 +10351,22 @@ react-router@5.2.0:
     tiny-invariant "^1.0.2"
     tiny-warning "^1.0.0"
 
+react-split-pane@0.1.92:
+  version "0.1.92"
+  resolved "https://registry.yarnpkg.com/react-split-pane/-/react-split-pane-0.1.92.tgz#68242f72138aed95dd5910eeb9d99822c4fc3a41"
+  integrity sha512-GfXP1xSzLMcLJI5BM36Vh7GgZBpy+U/X0no+VM3fxayv+p1Jly5HpMofZJraeaMl73b3hvlr+N9zJKvLB/uz9w==
+  dependencies:
+    prop-types "^15.7.2"
+    react-lifecycles-compat "^3.0.4"
+    react-style-proptype "^3.2.2"
+
+react-style-proptype@^3.2.2:
+  version "3.2.2"
+  resolved "https://registry.yarnpkg.com/react-style-proptype/-/react-style-proptype-3.2.2.tgz#d8e998e62ce79ec35b087252b90f19f1c33968a0"
+  integrity sha512-ywYLSjNkxKHiZOqNlso9PZByNEY+FTyh3C+7uuziK0xFXu9xzdyfHwg4S9iyiRRoPCR4k2LqaBBsWVmSBwCWYQ==
+  dependencies:
+    prop-types "^15.5.4"
+
 react-transition-group@4.3.0:
   version "4.3.0"
   resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.3.0.tgz#fea832e386cf8796c58b61874a3319704f5ce683"
@@ -9791,6 +10587,11 @@ regenerator-runtime@^0.13.10:
   resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz#ed07b19616bcbec5da6274ebc75ae95634bfc2ee"
   integrity sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw==
 
+regenerator-runtime@^0.13.11:
+  version "0.13.11"
+  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
+  integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
+
 regenerator-runtime@^0.13.4:
   version "0.13.7"
   resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
@@ -9826,6 +10627,20 @@ regexp.prototype.flags@^1.2.0:
     call-bind "^1.0.2"
     define-properties "^1.1.3"
 
+regexp.prototype.flags@^1.4.3:
+  version "1.4.3"
+  resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac"
+  integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.3"
+    functions-have-names "^1.2.2"
+
+regexpp@^3.2.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
+  integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==
+
 regexpu-core@^4.7.1:
   version "4.7.1"
   resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.1.tgz#2dea5a9a07233298fbf0db91fa9abc4c6e0f8ad6"
@@ -9904,7 +10719,7 @@ request-promise-native@^1.0.5:
     stealthy-require "^1.1.1"
     tough-cookie "^2.3.3"
 
-request@^2.83.0, request@^2.87.0, request@^2.88.0:
+request@^2.87.0, request@^2.88.0:
   version "2.88.2"
   resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
   integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
@@ -10010,6 +10825,24 @@ resolve@1.x, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.20.0, resolve@^1.3.2:
     is-core-module "^2.2.0"
     path-parse "^1.0.6"
 
+resolve@^1.22.0, resolve@^1.22.1:
+  version "1.22.1"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
+  integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
+  dependencies:
+    is-core-module "^2.9.0"
+    path-parse "^1.0.7"
+    supports-preserve-symlinks-flag "^1.0.0"
+
+resolve@^2.0.0-next.3:
+  version "2.0.0-next.4"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660"
+  integrity sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==
+  dependencies:
+    is-core-module "^2.9.0"
+    path-parse "^1.0.7"
+    supports-preserve-symlinks-flag "^1.0.0"
+
 restore-cursor@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
@@ -10028,12 +10861,10 @@ retry@^0.10.0:
   resolved "https://registry.yarnpkg.com/retry/-/retry-0.10.1.tgz#e76388d217992c252750241d3d3956fed98d8ff4"
   integrity sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=
 
-rimraf@2, rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3:
-  version "2.7.1"
-  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
-  integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
-  dependencies:
-    glob "^7.1.3"
+reusify@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
+  integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
 
 rimraf@2.6.2:
   version "2.6.2"
@@ -10042,6 +10873,20 @@ rimraf@2.6.2:
   dependencies:
     glob "^7.0.5"
 
+rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3:
+  version "2.7.1"
+  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
+  integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
+  dependencies:
+    glob "^7.1.3"
+
+rimraf@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
+  integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
+  dependencies:
+    glob "^7.1.3"
+
 ripemd160@^2.0.0, ripemd160@^2.0.1:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c"
@@ -10060,6 +10905,13 @@ run-async@^2.2.0:
   resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455"
   integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==
 
+run-parallel@^1.1.9:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
+  integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
+  dependencies:
+    queue-microtask "^1.2.2"
+
 run-queue@^1.0.0, run-queue@^1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47"
@@ -10084,6 +10936,15 @@ safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1,
   resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
   integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
 
+safe-regex-test@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295"
+  integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==
+  dependencies:
+    call-bind "^1.0.2"
+    get-intrinsic "^1.1.3"
+    is-regex "^1.1.4"
+
 safe-regex@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e"
@@ -10112,28 +10973,6 @@ sane@^2.0.0:
   optionalDependencies:
     fsevents "^1.2.3"
 
-sass-graph@^2.2.4:
-  version "2.2.6"
-  resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.6.tgz#09fda0e4287480e3e4967b72a2d133ba09b8d827"
-  integrity sha512-MKuEYXFSGuRSi8FZ3A7imN1CeVn9Gpw0/SFJKdL1ejXJneI9a5rwlEZrKejhEFAA3O6yr3eIyl/WuvASvlT36g==
-  dependencies:
-    glob "^7.0.0"
-    lodash "^4.0.0"
-    scss-tokenizer "^0.2.3"
-    yargs "^7.0.0"
-
-sass-loader@7.1.0:
-  version "7.1.0"
-  resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-7.1.0.tgz#16fd5138cb8b424bf8a759528a1972d72aad069d"
-  integrity sha512-+G+BKGglmZM2GUSfT9TLuEp6tzehHPjAMoRRItOojWIqIGPloVCMhNIQuG639eJ+y033PaGTSjLaTHts8Kw79w==
-  dependencies:
-    clone-deep "^2.0.1"
-    loader-utils "^1.0.1"
-    lodash.tail "^4.1.1"
-    neo-async "^2.5.0"
-    pify "^3.0.0"
-    semver "^5.5.0"
-
 sax@^1.2.4:
   version "1.2.4"
   resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
@@ -10164,14 +11003,6 @@ schema-utils@^1.0.0:
     ajv-errors "^1.0.0"
     ajv-keywords "^3.1.0"
 
-scss-tokenizer@^0.2.3:
-  version "0.2.3"
-  resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1"
-  integrity sha512-dYE8LhncfBUar6POCxMTm0Ln+erjeczqEvCJib5/7XNkdw1FkUGgwMPY360FY0FgPWQxHWCx29Jl3oejyGLM9Q==
-  dependencies:
-    js-base64 "^2.1.8"
-    source-map "^0.4.2"
-
 select-hose@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
@@ -10201,10 +11032,12 @@ semver@^7.3.4:
   dependencies:
     lru-cache "^6.0.0"
 
-semver@~5.3.0:
-  version "5.3.0"
-  resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
-  integrity sha512-mfmm3/H9+67MCVix1h+IXTpDwL6710LyHuk7+cWC9T1mE0qz4iHhh6r4hU2wrIT9iTsAAC2XQRvfblL028cpLw==
+semver@^7.3.7:
+  version "7.3.8"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798"
+  integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==
+  dependencies:
+    lru-cache "^6.0.0"
 
 send@0.17.1:
   version "0.17.1"
@@ -10298,15 +11131,6 @@ sha.js@^2.4.0, sha.js@^2.4.8:
     inherits "^2.0.1"
     safe-buffer "^5.0.1"
 
-shallow-clone@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-1.0.0.tgz#4480cd06e882ef68b2ad88a3ea54832e2c48b571"
-  integrity sha512-oeXreoKR/SyNJtRJMAKPDSvd28OqEwG4eR/xc856cRGBII7gX9lvAqDxusPm0846z/w/hWYjI1NpKwJ00NHzRA==
-  dependencies:
-    is-extendable "^0.1.1"
-    kind-of "^5.0.0"
-    mixin-object "^2.0.1"
-
 shallow-clone@^3.0.0:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3"
@@ -10321,11 +11145,23 @@ shebang-command@^1.2.0:
   dependencies:
     shebang-regex "^1.0.0"
 
+shebang-command@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
+  integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
+  dependencies:
+    shebang-regex "^3.0.0"
+
 shebang-regex@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
   integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
 
+shebang-regex@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
+  integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
+
 shellwords@^0.1.1:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
@@ -10360,6 +11196,11 @@ slash@^2.0.0:
   resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44"
   integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==
 
+slash@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
+  integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
+
 slide@^1.1.6:
   version "1.1.6"
   resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707"
@@ -10479,13 +11320,6 @@ source-map-url@^0.4.0:
   resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56"
   integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==
 
-source-map@^0.4.2:
-  version "0.4.4"
-  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"
-  integrity sha512-Y8nIfcb1s/7DcobUz1yOO1GSp7gyL+D9zLHDehT7iRESqGSxjJ448Sg7rvfgsRJCnKLdSl11uGf0s9X80cH0/A==
-  dependencies:
-    amdefine ">=0.0.4"
-
 source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7:
   version "0.5.7"
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
@@ -10632,13 +11466,6 @@ static-extend@^0.1.1:
   resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
   integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
 
-stdout-stream@^1.4.0:
-  version "1.4.1"
-  resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.1.tgz#5ac174cdd5cd726104aa0c0b2bd83815d8d535de"
-  integrity sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==
-  dependencies:
-    readable-stream "^2.0.1"
-
 stealthy-require@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b"
@@ -10689,7 +11516,7 @@ string-length@^2.0.0:
     astral-regex "^1.0.0"
     strip-ansi "^4.0.0"
 
-string-width@^1.0.1, string-width@^1.0.2:
+string-width@^1.0.1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
   integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=
@@ -10715,6 +11542,29 @@ string-width@^3.0.0, string-width@^3.1.0:
     is-fullwidth-code-point "^2.0.0"
     strip-ansi "^5.1.0"
 
+string.prototype.matchall@^4.0.7:
+  version "4.0.8"
+  resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz#3bf85722021816dcd1bf38bb714915887ca79fd3"
+  integrity sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.4"
+    es-abstract "^1.20.4"
+    get-intrinsic "^1.1.3"
+    has-symbols "^1.0.3"
+    internal-slot "^1.0.3"
+    regexp.prototype.flags "^1.4.3"
+    side-channel "^1.0.4"
+
+string.prototype.trim@^1.2.7:
+  version "1.2.7"
+  resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533"
+  integrity sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.4"
+    es-abstract "^1.20.4"
+
 string.prototype.trimend@^1.0.4:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80"
@@ -10723,6 +11573,15 @@ string.prototype.trimend@^1.0.4:
     call-bind "^1.0.2"
     define-properties "^1.1.3"
 
+string.prototype.trimend@^1.0.6:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533"
+  integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.4"
+    es-abstract "^1.20.4"
+
 string.prototype.trimstart@^1.0.4:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed"
@@ -10731,6 +11590,15 @@ string.prototype.trimstart@^1.0.4:
     call-bind "^1.0.2"
     define-properties "^1.1.3"
 
+string.prototype.trimstart@^1.0.6:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4"
+  integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.4"
+    es-abstract "^1.20.4"
+
 string_decoder@^1.0.0, string_decoder@^1.1.1:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
@@ -10766,6 +11634,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
   dependencies:
     ansi-regex "^4.1.0"
 
+strip-ansi@^6.0.1:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+  integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+  dependencies:
+    ansi-regex "^5.0.1"
+
 strip-bom@3.0.0, strip-bom@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
@@ -10802,6 +11677,11 @@ strip-indent@^3.0.0:
   dependencies:
     min-indent "^1.0.0"
 
+strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
+  integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
+
 strong-log-transformer@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz#0f5ed78d325e0421ac6f90f7f10e691d6ae3ae10"
@@ -10855,6 +11735,18 @@ supports-color@^6.1.0:
   dependencies:
     has-flag "^3.0.0"
 
+supports-color@^7.1.0:
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+  integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+  dependencies:
+    has-flag "^4.0.0"
+
+supports-preserve-symlinks-flag@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
+  integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
+
 symbol-tree@^3.2.2:
   version "3.2.4"
   resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
@@ -10865,15 +11757,6 @@ tapable@^1.0.0, tapable@^1.1.0:
   resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
   integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==
 
-tar@^2.0.0:
-  version "2.2.2"
-  resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.2.tgz#0ca8848562c7299b8b446ff6a4d60cdbb23edc40"
-  integrity sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==
-  dependencies:
-    block-stream "*"
-    fstream "^1.0.12"
-    inherits "2"
-
 tar@^4.4.10, tar@^4.4.12, tar@^4.4.8:
   version "4.4.13"
   resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525"
@@ -10967,6 +11850,11 @@ text-extensions@^1.0.0:
   resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26"
   integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==
 
+text-table@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
+  integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
+
 thenify-all@^1.0.0:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726"
@@ -11145,13 +12033,6 @@ trim-right@^1.0.1:
   resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
   integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=
 
-"true-case-path@^1.0.2":
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-1.0.3.tgz#f813b5a8c86b40da59606722b144e3225799f47d"
-  integrity sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==
-  dependencies:
-    glob "^7.1.2"
-
 ts-jest@23.10.5:
   version "23.10.5"
   resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-23.10.5.tgz#cdb550df4466a30489bf70ba867615799f388dd5"
@@ -11178,6 +12059,16 @@ ts-loader@5.2.1:
     micromatch "^3.1.4"
     semver "^5.0.1"
 
+tsconfig-paths@^3.14.1:
+  version "3.14.2"
+  resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088"
+  integrity sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==
+  dependencies:
+    "@types/json5" "^0.0.29"
+    json5 "^1.0.2"
+    minimist "^1.2.6"
+    strip-bom "^3.0.0"
+
 tslib@1.9.0:
   version "1.9.0"
   resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.0.tgz#e37a86fda8cbbaf23a057f473c9f4dc64e5fc2e8"
@@ -11293,7 +12184,7 @@ tsutils@^2.13.1, tsutils@^2.27.2, tsutils@^2.29.0:
   dependencies:
     tslib "^1.8.1"
 
-tsutils@^3.0.0, tsutils@^3.5.0:
+tsutils@^3.0.0, tsutils@^3.21.0, tsutils@^3.5.0:
   version "3.21.0"
   resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"
   integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==
@@ -11317,6 +12208,13 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0:
   resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
   integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
 
+type-check@^0.4.0, type-check@~0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
+  integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==
+  dependencies:
+    prelude-ls "^1.2.1"
+
 type-check@~0.3.2:
   version "0.3.2"
   resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
@@ -11329,6 +12227,11 @@ type-fest@^0.18.0:
   resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f"
   integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==
 
+type-fest@^0.20.2:
+  version "0.20.2"
+  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
+  integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
+
 type-fest@^0.3.0:
   version "0.3.1"
   resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1"
@@ -11352,6 +12255,15 @@ type-is@~1.6.17, type-is@~1.6.18:
     media-typer "0.3.0"
     mime-types "~2.1.24"
 
+typed-array-length@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb"
+  integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==
+  dependencies:
+    call-bind "^1.0.2"
+    for-each "^0.3.3"
+    is-typed-array "^1.1.9"
+
 typedarray@^0.0.6:
   version "0.0.6"
   resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
@@ -11404,6 +12316,16 @@ unbox-primitive@^1.0.1:
     has-symbols "^1.0.2"
     which-boxed-primitive "^1.0.2"
 
+unbox-primitive@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e"
+  integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==
+  dependencies:
+    call-bind "^1.0.2"
+    has-bigints "^1.0.2"
+    has-symbols "^1.0.3"
+    which-boxed-primitive "^1.0.2"
+
 unicode-canonical-property-names-ecmascript@^1.0.4:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"
@@ -11881,23 +12803,37 @@ which-boxed-primitive@^1.0.2:
     is-string "^1.0.5"
     is-symbol "^1.0.3"
 
-which-module@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f"
-  integrity sha512-F6+WgncZi/mJDrammbTuHe1q0R5hOXv/mBaiNA2TCNT/LTHusX0V+CJnj9XT8ki5ln2UZyyddDgHfCzyrOH7MQ==
-
 which-module@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
   integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
 
-which@1, which@^1.2.12, which@^1.2.14, which@^1.2.9, which@^1.3.0, which@^1.3.1:
+which-typed-array@^1.1.9:
+  version "1.1.9"
+  resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6"
+  integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==
+  dependencies:
+    available-typed-arrays "^1.0.5"
+    call-bind "^1.0.2"
+    for-each "^0.3.3"
+    gopd "^1.0.1"
+    has-tostringtag "^1.0.0"
+    is-typed-array "^1.1.10"
+
+which@^1.2.12, which@^1.2.14, which@^1.2.9, which@^1.3.0, which@^1.3.1:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
   integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
   dependencies:
     isexe "^2.0.0"
 
+which@^2.0.1:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
+  integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
+  dependencies:
+    isexe "^2.0.0"
+
 wide-align@^1.1.0:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457"
@@ -11912,7 +12848,7 @@ windows-release@^3.1.0:
   dependencies:
     execa "^1.0.0"
 
-word-wrap@~1.2.3:
+word-wrap@^1.2.3, word-wrap@~1.2.3:
   version "1.2.3"
   resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
   integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
@@ -12089,14 +13025,6 @@ yargs-parser@^20.2.3:
   resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
   integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
 
-yargs-parser@^5.0.1:
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.1.tgz#7ede329c1d8cdbbe209bd25cdb990e9b1ebbb394"
-  integrity sha512-wpav5XYiddjXxirPoCTUPbqM0PXvJ9hiBMvuJgInvo4/lAOTZzUprArw17q2O1P2+GHhbBr18/iQwjL5Z9BqfA==
-  dependencies:
-    camelcase "^3.0.0"
-    object.assign "^4.1.0"
-
 yargs-parser@^9.0.2:
   version "9.0.2"
   resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-9.0.2.tgz#9ccf6a43460fe4ed40a9bb68f48d43b8a68cc077"
@@ -12175,21 +13103,7 @@ yargs@^14.2.2:
     y18n "^4.0.0"
     yargs-parser "^15.0.1"
 
-yargs@^7.0.0:
-  version "7.1.2"
-  resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.2.tgz#63a0a5d42143879fdbb30370741374e0641d55db"
-  integrity sha512-ZEjj/dQYQy0Zx0lgLMLR8QuaqTihnxirir7EwUHp1Axq4e3+k8jXU5K0VLbNvedv1f4EWtBonDIZm0NUr+jCcA==
-  dependencies:
-    camelcase "^3.0.0"
-    cliui "^3.2.0"
-    decamelize "^1.1.1"
-    get-caller-file "^1.0.1"
-    os-locale "^1.4.0"
-    read-pkg-up "^1.0.1"
-    require-directory "^2.1.1"
-    require-main-filename "^1.0.1"
-    set-blocking "^2.0.0"
-    string-width "^1.0.2"
-    which-module "^1.0.0"
-    y18n "^3.2.1"
-    yargs-parser "^5.0.1"
+yocto-queue@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
+  integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==