diff options
author | sai-neetha <sai-neetha.phulmali@highstreet-technologies.com> | 2023-03-20 08:05:47 +0100 |
---|---|---|
committer | highstreetherbert <herbert.eiselt@highstreet-technologies.com> | 2023-03-29 19:06:25 +0200 |
commit | 15e2d3a29b0d1a304965e34f114a911e5a7abdb3 (patch) | |
tree | 711ef5616aceb115a1081cccd152eeae0e87bc79 /sdnr | |
parent | ac5e2dc8f1ee4d5549f7260374e8164d52b07f55 (diff) |
Odlux Update
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>
Diffstat (limited to 'sdnr')
267 files changed, 8010 insertions, 5719 deletions
diff --git a/sdnr/wt/odlux/.eslintignore b/sdnr/wt/odlux/.eslintignore new file mode 100644 index 000000000..e5c39402f --- /dev/null +++ b/sdnr/wt/odlux/.eslintignore @@ -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 diff --git a/sdnr/wt/odlux/apps/apiDemo/package.json b/sdnr/wt/odlux/apps/apiDemo/package.json index f01a396e8..ff9e3c47e 100644 --- a/sdnr/wt/odlux/apps/apiDemo/package.json +++ b/sdnr/wt/odlux/apps/apiDemo/package.json @@ -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", diff --git a/sdnr/wt/odlux/apps/apiDemo/pom.xml b/sdnr/wt/odlux/apps/apiDemo/pom.xml index b6dc53e93..97f7faa80 100644 --- a/sdnr/wt/odlux/apps/apiDemo/pom.xml +++ b/sdnr/wt/odlux/apps/apiDemo/pom.xml @@ -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> @@ -139,7 +140,7 @@ <!-- 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/apiDemo/src/handlers/apiDemoRootHandler.ts b/sdnr/wt/odlux/apps/apiDemo/src/handlers/apiDemoRootHandler.ts index 36688b3a5..128a03286 100644 --- a/sdnr/wt/odlux/apps/apiDemo/src/handlers/apiDemoRootHandler.ts +++ b/sdnr/wt/odlux/apps/apiDemo/src/handlers/apiDemoRootHandler.ts @@ -18,22 +18,23 @@ 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); diff --git a/sdnr/wt/odlux/apps/apiDemo/src/handlers/modulesHandler.ts b/sdnr/wt/odlux/apps/apiDemo/src/handlers/modulesHandler.ts index 8777355db..1984a2d10 100644 --- a/sdnr/wt/odlux/apps/apiDemo/src/handlers/modulesHandler.ts +++ b/sdnr/wt/odlux/apps/apiDemo/src/handlers/modulesHandler.ts @@ -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 = []; diff --git a/sdnr/wt/odlux/apps/apiDemo/src/models/module.ts b/sdnr/wt/odlux/apps/apiDemo/src/models/module.ts index abf86b76e..48772a785 100644 --- a/sdnr/wt/odlux/apps/apiDemo/src/models/module.ts +++ b/sdnr/wt/odlux/apps/apiDemo/src/models/module.ts @@ -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 diff --git a/sdnr/wt/odlux/apps/apiDemo/src/plugin.tsx b/sdnr/wt/odlux/apps/apiDemo/src/plugin.tsx index 076eb5c70..2f70d8e2d 100644 --- a/sdnr/wt/odlux/apps/apiDemo/src/plugin.tsx +++ b/sdnr/wt/odlux/apps/apiDemo/src/plugin.tsx @@ -15,20 +15,20 @@ * 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', }); diff --git a/sdnr/wt/odlux/apps/apiDemo/tsconfig.json b/sdnr/wt/odlux/apps/apiDemo/tsconfig.json index a66b5d828..ca65092e0 100644 --- a/sdnr/wt/odlux/apps/apiDemo/tsconfig.json +++ b/sdnr/wt/odlux/apps/apiDemo/tsconfig.json @@ -4,7 +4,7 @@ "outDir": "./dist", "sourceMap": true, "forceConsistentCasingInFileNames": true, - "allowSyntheticDefaultImports": false, + "allowSyntheticDefaultImports": true, "allowUnreachableCode": false, "allowUnusedLabels": false, "noFallthroughCasesInSwitch": true, diff --git a/sdnr/wt/odlux/apps/configurationApp/package.json b/sdnr/wt/odlux/apps/configurationApp/package.json index afd5456ec..b1d7d95d5 100644 --- a/sdnr/wt/odlux/apps/configurationApp/package.json +++ b/sdnr/wt/odlux/apps/configurationApp/package.json @@ -26,7 +26,11 @@ "@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", diff --git a/sdnr/wt/odlux/apps/configurationApp/pom.xml b/sdnr/wt/odlux/apps/configurationApp/pom.xml index 63475625d..9703e2924 100644 --- a/sdnr/wt/odlux/apps/configurationApp/pom.xml +++ b/sdnr/wt/odlux/apps/configurationApp/pom.xml @@ -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> @@ -139,7 +140,7 @@ <!-- 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/configurationApp/src/actions/deviceActions.ts b/sdnr/wt/odlux/apps/configurationApp/src/actions/deviceActions.ts index 37583787f..52137135a 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/actions/deviceActions.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/actions/deviceActions.ts @@ -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 index 000000000..1b74cc479 --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/assets/icons/configurationAppIcon.svg @@ -0,0 +1,20 @@ +<!-- 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" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 142 140" >
+<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" transform="scale(10,10)">
+
+
+ <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>
+
+ <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>
+
+</g>
+</svg>
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/src/components/baseProps.ts b/sdnr/wt/odlux/apps/configurationApp/src/components/baseProps.ts index 26c3944c9..7187c0a4e 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/components/baseProps.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/components/baseProps.ts @@ -16,13 +16,13 @@ * ============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 diff --git a/sdnr/wt/odlux/apps/configurationApp/src/components/ifWhenTextInput.tsx b/sdnr/wt/odlux/apps/configurationApp/src/components/ifWhenTextInput.tsx index 8ce3106a6..b176e5db5 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/components/ifWhenTextInput.tsx +++ b/sdnr/wt/odlux/apps/configurationApp/src/components/ifWhenTextInput.tsx @@ -16,82 +16,86 @@ * ============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 diff --git a/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementBoolean.tsx b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementBoolean.tsx index 81c9d6dcd..56fb93cea 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementBoolean.tsx +++ b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementBoolean.tsx @@ -16,43 +16,48 @@ * ============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 diff --git a/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementLeafList.tsx b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementLeafList.tsx index 5937ed7b3..669ddff63 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementLeafList.tsx +++ b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementLeafList.tsx @@ -16,19 +16,23 @@ * ============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> </> diff --git a/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementNumber.tsx b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementNumber.tsx index 76c11f6e5..b0342788f 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementNumber.tsx +++ b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementNumber.tsx @@ -16,14 +16,14 @@ * ============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 }} diff --git a/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementReference.tsx b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementReference.tsx index 9e863f0d0..e3bb8f048 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementReference.tsx +++ b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementReference.tsx @@ -17,16 +17,16 @@ */ 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 diff --git a/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementSelection.tsx b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementSelection.tsx index fdf803419..ebd04dab4 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementSelection.tsx +++ b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementSelection.tsx @@ -16,45 +16,54 @@ * ============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 diff --git a/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementString.tsx b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementString.tsx index 4908c41aa..8381d99a4 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementString.tsx +++ b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementString.tsx @@ -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 }} diff --git a/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementUnion.tsx b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementUnion.tsx index 67cd998d7..8d232f5ee 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementUnion.tsx +++ b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementUnion.tsx @@ -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 index 0a95cd8ca..000000000 --- a/sdnr/wt/odlux/apps/configurationApp/src/components/verifyer.ts +++ /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 diff --git a/sdnr/wt/odlux/apps/configurationApp/src/handlers/configurationAppRootHandler.ts b/sdnr/wt/odlux/apps/configurationApp/src/handlers/configurationAppRootHandler.ts index 1af699a6b..9cbd9163e 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/handlers/configurationAppRootHandler.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/handlers/configurationAppRootHandler.ts @@ -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; } } diff --git a/sdnr/wt/odlux/apps/configurationApp/src/handlers/connectedNetworkElementsHandler.ts b/sdnr/wt/odlux/apps/configurationApp/src/handlers/connectedNetworkElementsHandler.ts index 8ca8fdf27..d2863dd2e 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/handlers/connectedNetworkElementsHandler.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/handlers/connectedNetworkElementsHandler.ts @@ -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); - } + }, ); diff --git a/sdnr/wt/odlux/apps/configurationApp/src/handlers/deviceDescriptionHandler.ts b/sdnr/wt/odlux/apps/configurationApp/src/handlers/deviceDescriptionHandler.ts index 408399da4..cd01b0988 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/handlers/deviceDescriptionHandler.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/handlers/deviceDescriptionHandler.ts @@ -16,23 +16,23 @@ * ============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; diff --git a/sdnr/wt/odlux/apps/configurationApp/src/handlers/valueSelectorHandler.ts b/sdnr/wt/odlux/apps/configurationApp/src/handlers/valueSelectorHandler.ts index 5b2d55ee2..70d5eb253 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/handlers/valueSelectorHandler.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/handlers/valueSelectorHandler.ts @@ -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: [], }; } diff --git a/sdnr/wt/odlux/apps/configurationApp/src/handlers/viewDescriptionHandler.ts b/sdnr/wt/odlux/apps/configurationApp/src/handlers/viewDescriptionHandler.ts index ff85a97ea..39b47be84 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/handlers/viewDescriptionHandler.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/handlers/viewDescriptionHandler.ts @@ -16,18 +16,18 @@ * ============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, diff --git a/sdnr/wt/odlux/apps/configurationApp/src/models/networkElementConnection.ts b/sdnr/wt/odlux/apps/configurationApp/src/models/networkElementConnection.ts index 88f70181c..e1ef1ea2d 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/models/networkElementConnection.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/models/networkElementConnection.ts @@ -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; }[]; - } -} + }; +}; diff --git a/sdnr/wt/odlux/apps/configurationApp/src/models/uiModels.ts b/sdnr/wt/odlux/apps/configurationApp/src/models/uiModels.ts index 29484d812..7d9e63caf 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/models/uiModels.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/models/uiModels.ts @@ -16,128 +16,130 @@ * ============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 diff --git a/sdnr/wt/odlux/apps/configurationApp/src/models/yang.ts b/sdnr/wt/odlux/apps/configurationApp/src/models/yang.ts index 79704ae34..e4e59fb96 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/models/yang.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/models/yang.ts @@ -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 diff --git a/sdnr/wt/odlux/apps/configurationApp/src/pluginConfiguration.tsx b/sdnr/wt/odlux/apps/configurationApp/src/pluginConfiguration.tsx index e37879102..7dd2d6ae4 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/pluginConfiguration.tsx +++ b/sdnr/wt/odlux/apps/configurationApp/src/pluginConfiguration.tsx @@ -16,71 +16,74 @@ * ============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', }); } diff --git a/sdnr/wt/odlux/apps/configurationApp/src/services/restServices.ts b/sdnr/wt/odlux/apps/configurationApp/src/services/restServices.ts index 02060ef12..07e263559 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/services/restServices.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/services/restServices.ts @@ -16,79 +16,79 @@ * ============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' }); } } diff --git a/sdnr/wt/odlux/apps/configurationApp/src/services/yangService.ts b/sdnr/wt/odlux/apps/configurationApp/src/services/yangService.ts index b81a92c14..bbd051aeb 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/services/yangService.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/services/yangService.ts @@ -16,28 +16,22 @@ * ============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 index 000000000..9dd12031f --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/utilities/verifyer.ts @@ -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 index 000000000..ad34c83b9 --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/utilities/viewEngineHelper.ts @@ -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 diff --git a/sdnr/wt/odlux/apps/configurationApp/src/views/configurationApplication.tsx b/sdnr/wt/odlux/apps/configurationApp/src/views/configurationApplication.tsx index 0e2ddb395..0f143d818 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/views/configurationApplication.tsx +++ b/sdnr/wt/odlux/apps/configurationApp/src/views/configurationApplication.tsx @@ -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; diff --git a/sdnr/wt/odlux/apps/configurationApp/src/views/networkElementSelector.tsx b/sdnr/wt/odlux/apps/configurationApp/src/views/networkElementSelector.tsx index 1a1008dad..e96f40d61 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/views/networkElementSelector.tsx +++ b/sdnr/wt/odlux/apps/configurationApp/src/views/networkElementSelector.tsx @@ -16,15 +16,15 @@ * ============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 index 000000000..fa2968c9c --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/yang/whenParser.ts @@ -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 diff --git a/sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts b/sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts index e8e636f9b..cc2520100 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts @@ -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 @@ -15,14 +18,30 @@ * 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 diff --git a/sdnr/wt/odlux/apps/configurationApp/webpack.config.js b/sdnr/wt/odlux/apps/configurationApp/webpack.config.js index 57caf079f..0d37c7d87 100644 --- a/sdnr/wt/odlux/apps/configurationApp/webpack.config.js +++ b/sdnr/wt/odlux/apps/configurationApp/webpack.config.js @@ -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, } }]; } diff --git a/sdnr/wt/odlux/apps/connectApp/package.json b/sdnr/wt/odlux/apps/connectApp/package.json index a31824ae2..9a3c35baa 100644 --- a/sdnr/wt/odlux/apps/connectApp/package.json +++ b/sdnr/wt/odlux/apps/connectApp/package.json @@ -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", diff --git a/sdnr/wt/odlux/apps/connectApp/pom.xml b/sdnr/wt/odlux/apps/connectApp/pom.xml index c12048ebb..c9509c7a4 100644 --- a/sdnr/wt/odlux/apps/connectApp/pom.xml +++ b/sdnr/wt/odlux/apps/connectApp/pom.xml @@ -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> @@ -139,7 +140,7 @@ <!-- 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/connectApp/src/actions/commonNetworkElementsActions.ts b/sdnr/wt/odlux/apps/connectApp/src/actions/commonNetworkElementsActions.ts index 26aa8d2d7..948f2aada 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/actions/commonNetworkElementsActions.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/actions/commonNetworkElementsActions.ts @@ -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======================================================================== @@ -19,16 +17,22 @@ * ============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); } }; diff --git a/sdnr/wt/odlux/apps/connectApp/src/actions/infoNetworkElementActions.ts b/sdnr/wt/odlux/apps/connectApp/src/actions/infoNetworkElementActions.ts index bb744e236..120f9916f 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/actions/infoNetworkElementActions.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/actions/infoNetworkElementActions.ts @@ -15,68 +15,68 @@ * 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 diff --git a/sdnr/wt/odlux/apps/connectApp/src/actions/mountedNetworkElementsActions.ts b/sdnr/wt/odlux/apps/connectApp/src/actions/mountedNetworkElementsActions.ts index 26ee7674f..11bac10e4 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/actions/mountedNetworkElementsActions.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/actions/mountedNetworkElementsActions.ts @@ -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' } })); } diff --git a/sdnr/wt/odlux/apps/connectApp/src/actions/networkElementsActions.ts b/sdnr/wt/odlux/apps/connectApp/src/actions/networkElementsActions.ts index 57f036e56..d22a6c645 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/actions/networkElementsActions.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/actions/networkElementsActions.ts @@ -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()); }; diff --git a/sdnr/wt/odlux/apps/connectApp/src/actions/tlsKeyActions.ts b/sdnr/wt/odlux/apps/connectApp/src/actions/tlsKeyActions.ts index 1da16d9ad..65d23c439 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/actions/tlsKeyActions.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/actions/tlsKeyActions.ts @@ -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 index 000000000..5aca4fae7 --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src/assets/icons/connectAppIcon.svg @@ -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> diff --git a/sdnr/wt/odlux/apps/connectApp/src/components/connectionStatusLog.tsx b/sdnr/wt/odlux/apps/connectApp/src/components/connectionStatusLog.tsx index b240b2419..6a8c92438 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/components/connectionStatusLog.tsx +++ b/sdnr/wt/odlux/apps/connectApp/src/components/connectionStatusLog.tsx @@ -15,11 +15,13 @@ * 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(); } diff --git a/sdnr/wt/odlux/apps/connectApp/src/components/editNetworkElementDialog.tsx b/sdnr/wt/odlux/apps/connectApp/src/components/editNetworkElementDialog.tsx index 5740ebda0..b0db63476 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/components/editNetworkElementDialog.tsx +++ b/sdnr/wt/odlux/apps/connectApp/src/components/editNetworkElementDialog.tsx @@ -15,42 +15,43 @@ * 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; } } diff --git a/sdnr/wt/odlux/apps/connectApp/src/components/infoNetworkElementDialog.tsx b/sdnr/wt/odlux/apps/connectApp/src/components/infoNetworkElementDialog.tsx index b0c7840be..4841b9389 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/components/infoNetworkElementDialog.tsx +++ b/sdnr/wt/odlux/apps/connectApp/src/components/infoNetworkElementDialog.tsx @@ -15,145 +15,146 @@ * 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 diff --git a/sdnr/wt/odlux/apps/connectApp/src/components/networkElements.tsx b/sdnr/wt/odlux/apps/connectApp/src/components/networkElements.tsx index 67fdef69d..1ce8f0c3b 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/components/networkElements.tsx +++ b/sdnr/wt/odlux/apps/connectApp/src/components/networkElements.tsx @@ -15,39 +15,37 @@ * 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)); diff --git a/sdnr/wt/odlux/apps/connectApp/src/components/refreshConnectionStatusLogDialog.tsx b/sdnr/wt/odlux/apps/connectApp/src/components/refreshConnectionStatusLogDialog.tsx index c09f59b40..a4aea7f82 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/components/refreshConnectionStatusLogDialog.tsx +++ b/sdnr/wt/odlux/apps/connectApp/src/components/refreshConnectionStatusLogDialog.tsx @@ -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); diff --git a/sdnr/wt/odlux/apps/connectApp/src/components/refreshNetworkElementsDialog.tsx b/sdnr/wt/odlux/apps/connectApp/src/components/refreshNetworkElementsDialog.tsx index abf593882..e41fd27aa 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/components/refreshNetworkElementsDialog.tsx +++ b/sdnr/wt/odlux/apps/connectApp/src/components/refreshNetworkElementsDialog.tsx @@ -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); diff --git a/sdnr/wt/odlux/apps/connectApp/src/handlers/connectAppRootHandler.ts b/sdnr/wt/odlux/apps/connectApp/src/handlers/connectAppRootHandler.ts index 6a1825224..b386dcdef 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/handlers/connectAppRootHandler.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/handlers/connectAppRootHandler.ts @@ -18,12 +18,13 @@ 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); diff --git a/sdnr/wt/odlux/apps/connectApp/src/handlers/connectionStatusLogHandler.ts b/sdnr/wt/odlux/apps/connectApp/src/handlers/connectionStatusLogHandler.ts index 6863ec33b..264b6c198 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/handlers/connectionStatusLogHandler.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/handlers/connectionStatusLogHandler.ts @@ -15,10 +15,11 @@ * 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 diff --git a/sdnr/wt/odlux/apps/connectApp/src/handlers/infoNetworkElementHandler.ts b/sdnr/wt/odlux/apps/connectApp/src/handlers/infoNetworkElementHandler.ts index 3e2d1cec1..692e63a5c 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/handlers/infoNetworkElementHandler.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/handlers/infoNetworkElementHandler.ts @@ -15,79 +15,78 @@ * 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 diff --git a/sdnr/wt/odlux/apps/connectApp/src/handlers/networkElementsHandler.ts b/sdnr/wt/odlux/apps/connectApp/src/handlers/networkElementsHandler.ts index b74a39427..42d2824b9 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/handlers/networkElementsHandler.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/handlers/networkElementsHandler.ts @@ -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); diff --git a/sdnr/wt/odlux/apps/connectApp/src/handlers/tlsKeyHandler.ts b/sdnr/wt/odlux/apps/connectApp/src/handlers/tlsKeyHandler.ts index 326b3cc8e..20badcba0 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/handlers/tlsKeyHandler.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/handlers/tlsKeyHandler.ts @@ -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, }; } } diff --git a/sdnr/wt/odlux/apps/connectApp/src/models/connectionStatusLog.ts b/sdnr/wt/odlux/apps/connectApp/src/models/connectionStatusLog.ts index df43c217a..82b49a081 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/models/connectionStatusLog.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/models/connectionStatusLog.ts @@ -23,5 +23,5 @@ export type ConnectionStatusLogType = { objectId: string; type: string; newValue: string; -} +}; diff --git a/sdnr/wt/odlux/apps/connectApp/src/models/guiCutTrough.ts b/sdnr/wt/odlux/apps/connectApp/src/models/guiCutTrough.ts index b9f515dc8..0fd46a82d 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/models/guiCutTrough.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/models/guiCutTrough.ts @@ -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 diff --git a/sdnr/wt/odlux/apps/connectApp/src/models/networkElementBase.ts b/sdnr/wt/odlux/apps/connectApp/src/models/networkElementBase.ts index 39278a8e1..a46a30e2b 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/models/networkElementBase.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/models/networkElementBase.ts @@ -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 diff --git a/sdnr/wt/odlux/apps/connectApp/src/models/networkElementConnection.ts b/sdnr/wt/odlux/apps/connectApp/src/models/networkElementConnection.ts index bc15afb31..bb076c720 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/models/networkElementConnection.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/models/networkElementConnection.ts @@ -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; +}; /** diff --git a/sdnr/wt/odlux/apps/connectApp/src/models/networkElementConnectionLog.ts b/sdnr/wt/odlux/apps/connectApp/src/models/networkElementConnectionLog.ts index a1535cbe5..4b4e28325 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/models/networkElementConnectionLog.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/models/networkElementConnectionLog.ts @@ -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; -} +}; diff --git a/sdnr/wt/odlux/apps/connectApp/src/models/panelId.ts b/sdnr/wt/odlux/apps/connectApp/src/models/panelId.ts index 2000d9407..2861f107f 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/models/panelId.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/models/panelId.ts @@ -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 diff --git a/sdnr/wt/odlux/apps/connectApp/src/models/topologyNetconf.ts b/sdnr/wt/odlux/apps/connectApp/src/models/topologyNetconf.ts index 936e20bc2..85a1a71fd 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/models/topologyNetconf.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/models/topologyNetconf.ts @@ -16,23 +16,23 @@ * ============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; } diff --git a/sdnr/wt/odlux/apps/connectApp/src/models/yangCapabilitiesType.ts b/sdnr/wt/odlux/apps/connectApp/src/models/yangCapabilitiesType.ts index c8cf7049c..b69993f7d 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/models/yangCapabilitiesType.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/models/yangCapabilitiesType.ts @@ -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 diff --git a/sdnr/wt/odlux/apps/connectApp/src/pluginConnect.tsx b/sdnr/wt/odlux/apps/connectApp/src/pluginConnect.tsx index 2a9a46d2b..c2907166c 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/pluginConnect.tsx +++ b/sdnr/wt/odlux/apps/connectApp/src/pluginConnect.tsx @@ -16,27 +16,25 @@ * ============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) { diff --git a/sdnr/wt/odlux/apps/connectApp/src/services/connectService.ts b/sdnr/wt/odlux/apps/connectApp/src/services/connectService.ts index 08cc58056..1d74f859a 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/services/connectService.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/services/connectService.ts @@ -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) { } diff --git a/sdnr/wt/odlux/apps/connectApp/src/views/connectView.tsx b/sdnr/wt/odlux/apps/connectApp/src/views/connectView.tsx index 082839718..a6fcb7c32 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/views/connectView.tsx +++ b/sdnr/wt/odlux/apps/connectApp/src/views/connectView.tsx @@ -15,113 +15,88 @@ * 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 diff --git a/sdnr/wt/odlux/apps/connectApp/tsconfig.json b/sdnr/wt/odlux/apps/connectApp/tsconfig.json index a66b5d828..ca65092e0 100644 --- a/sdnr/wt/odlux/apps/connectApp/tsconfig.json +++ b/sdnr/wt/odlux/apps/connectApp/tsconfig.json @@ -4,7 +4,7 @@ "outDir": "./dist", "sourceMap": true, "forceConsistentCasingInFileNames": true, - "allowSyntheticDefaultImports": false, + "allowSyntheticDefaultImports": true, "allowUnreachableCode": false, "allowUnusedLabels": false, "noFallthroughCasesInSwitch": true, diff --git a/sdnr/wt/odlux/apps/connectApp/webpack.config.js b/sdnr/wt/odlux/apps/connectApp/webpack.config.js index ff76904c5..b7aebb9df 100644 --- a/sdnr/wt/odlux/apps/connectApp/webpack.config.js +++ b/sdnr/wt/odlux/apps/connectApp/webpack.config.js @@ -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]' + } + }] }] }, diff --git a/sdnr/wt/odlux/apps/demoApp/package.json b/sdnr/wt/odlux/apps/demoApp/package.json index 951332a28..6a31bc3ec 100644 --- a/sdnr/wt/odlux/apps/demoApp/package.json +++ b/sdnr/wt/odlux/apps/demoApp/package.json @@ -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", diff --git a/sdnr/wt/odlux/apps/demoApp/pom.xml b/sdnr/wt/odlux/apps/demoApp/pom.xml index 07f990b79..172a43561 100644 --- a/sdnr/wt/odlux/apps/demoApp/pom.xml +++ b/sdnr/wt/odlux/apps/demoApp/pom.xml @@ -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> @@ -139,7 +140,7 @@ <!-- 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/demoApp/src/actions/authorActions.ts b/sdnr/wt/odlux/apps/demoApp/src/actions/authorActions.ts index f75075192..f22d1e0a3 100644 --- a/sdnr/wt/odlux/apps/demoApp/src/actions/authorActions.ts +++ b/sdnr/wt/odlux/apps/demoApp/src/actions/authorActions.ts @@ -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)); }); -} +}; diff --git a/sdnr/wt/odlux/apps/demoApp/src/components/counter.tsx b/sdnr/wt/odlux/apps/demoApp/src/components/counter.tsx index 6b960cdae..1aad97451 100644 --- a/sdnr/wt/odlux/apps/demoApp/src/components/counter.tsx +++ b/sdnr/wt/odlux/apps/demoApp/src/components/counter.tsx @@ -15,20 +15,15 @@ * 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 diff --git a/sdnr/wt/odlux/apps/demoApp/src/handlers/demoAppRootHandler.ts b/sdnr/wt/odlux/apps/demoApp/src/handlers/demoAppRootHandler.ts index 9ff8450c8..1f920f2a8 100644 --- a/sdnr/wt/odlux/apps/demoApp/src/handlers/demoAppRootHandler.ts +++ b/sdnr/wt/odlux/apps/demoApp/src/handlers/demoAppRootHandler.ts @@ -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; } } diff --git a/sdnr/wt/odlux/apps/demoApp/src/handlers/editAuthorHandler.ts b/sdnr/wt/odlux/apps/demoApp/src/handlers/editAuthorHandler.ts index 34b533cb1..1d37a36cc 100644 --- a/sdnr/wt/odlux/apps/demoApp/src/handlers/editAuthorHandler.ts +++ b/sdnr/wt/odlux/apps/demoApp/src/handlers/editAuthorHandler.ts @@ -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; }; diff --git a/sdnr/wt/odlux/apps/demoApp/src/handlers/listAuthorsHandler.ts b/sdnr/wt/odlux/apps/demoApp/src/handlers/listAuthorsHandler.ts index ca2b6d3c6..c85eaff04 100644 --- a/sdnr/wt/odlux/apps/demoApp/src/handlers/listAuthorsHandler.ts +++ b/sdnr/wt/odlux/apps/demoApp/src/handlers/listAuthorsHandler.ts @@ -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, }; } } diff --git a/sdnr/wt/odlux/apps/demoApp/src/models/author.ts b/sdnr/wt/odlux/apps/demoApp/src/models/author.ts index 0aaa308a2..bdd414cba 100644 --- a/sdnr/wt/odlux/apps/demoApp/src/models/author.ts +++ b/sdnr/wt/odlux/apps/demoApp/src/models/author.ts @@ -21,7 +21,7 @@ */ export interface IAuthor { /** - * Defines the unique id of the autor. + * Defines the unique id of the author. */ id: number; diff --git a/sdnr/wt/odlux/apps/demoApp/src/plugin.tsx b/sdnr/wt/odlux/apps/demoApp/src/plugin.tsx index 4d67c28ac..7b29b4045 100644 --- a/sdnr/wt/odlux/apps/demoApp/src/plugin.tsx +++ b/sdnr/wt/odlux/apps/demoApp/src/plugin.tsx @@ -15,13 +15,13 @@ * 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', }); } diff --git a/sdnr/wt/odlux/apps/demoApp/src/services/authorService.ts b/sdnr/wt/odlux/apps/demoApp/src/services/authorService.ts index 13e4b316c..deaa2ff76 100644 --- a/sdnr/wt/odlux/apps/demoApp/src/services/authorService.ts +++ b/sdnr/wt/odlux/apps/demoApp/src/services/authorService.ts @@ -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); } diff --git a/sdnr/wt/odlux/apps/demoApp/src/views/authorsList.tsx b/sdnr/wt/odlux/apps/demoApp/src/views/authorsList.tsx index b56058d36..5d9f13a55 100644 --- a/sdnr/wt/odlux/apps/demoApp/src/views/authorsList.tsx +++ b/sdnr/wt/odlux/apps/demoApp/src/views/authorsList.tsx @@ -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; diff --git a/sdnr/wt/odlux/apps/demoApp/src/views/editAuthor.tsx b/sdnr/wt/odlux/apps/demoApp/src/views/editAuthor.tsx index 92f671234..0da619ba2 100644 --- a/sdnr/wt/odlux/apps/demoApp/src/views/editAuthor.tsx +++ b/sdnr/wt/odlux/apps/demoApp/src/views/editAuthor.tsx @@ -15,10 +15,10 @@ * 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> - ) + ); } } diff --git a/sdnr/wt/odlux/apps/demoApp/tsconfig.json b/sdnr/wt/odlux/apps/demoApp/tsconfig.json index a66b5d828..ca65092e0 100644 --- a/sdnr/wt/odlux/apps/demoApp/tsconfig.json +++ b/sdnr/wt/odlux/apps/demoApp/tsconfig.json @@ -4,7 +4,7 @@ "outDir": "./dist", "sourceMap": true, "forceConsistentCasingInFileNames": true, - "allowSyntheticDefaultImports": false, + "allowSyntheticDefaultImports": true, "allowUnreachableCode": false, "allowUnusedLabels": false, "noFallthroughCasesInSwitch": true, diff --git a/sdnr/wt/odlux/apps/eventLogApp/pom.xml b/sdnr/wt/odlux/apps/eventLogApp/pom.xml index 19d9ad0c1..9e4ed5fe7 100644 --- a/sdnr/wt/odlux/apps/eventLogApp/pom.xml +++ b/sdnr/wt/odlux/apps/eventLogApp/pom.xml @@ -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> @@ -139,7 +140,7 @@ <!-- 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 index 000000000..743167d2c --- /dev/null +++ b/sdnr/wt/odlux/apps/eventLogApp/src/assets/icons/eventLogAppIcon.svg @@ -0,0 +1,21 @@ +<!-- 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" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 512 512">
+
+<g>
+<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"/>
+
+<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"/>
+
+<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
+ 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
+ c0,17.673-14.328,32-32,32s-32-14.327-32-32C224,46.326,238.328,32,256,32z M384,448H128V96h48v32h160V96h48V448z"/>
+</g>
+</svg>
diff --git a/sdnr/wt/odlux/apps/eventLogApp/src/pluginEventLog.tsx b/sdnr/wt/odlux/apps/eventLogApp/src/pluginEventLog.tsx index a2edb436f..8ee322a8e 100644 --- a/sdnr/wt/odlux/apps/eventLogApp/src/pluginEventLog.tsx +++ b/sdnr/wt/odlux/apps/eventLogApp/src/pluginEventLog.tsx @@ -17,24 +17,26 @@ */ // 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', }); } diff --git a/sdnr/wt/odlux/apps/eventLogApp/tsconfig.json b/sdnr/wt/odlux/apps/eventLogApp/tsconfig.json index a66b5d828..ca65092e0 100644 --- a/sdnr/wt/odlux/apps/eventLogApp/tsconfig.json +++ b/sdnr/wt/odlux/apps/eventLogApp/tsconfig.json @@ -4,7 +4,7 @@ "outDir": "./dist", "sourceMap": true, "forceConsistentCasingInFileNames": true, - "allowSyntheticDefaultImports": false, + "allowSyntheticDefaultImports": true, "allowUnreachableCode": false, "allowUnusedLabels": false, "noFallthroughCasesInSwitch": true, diff --git a/sdnr/wt/odlux/apps/eventLogApp/webpack.config.js b/sdnr/wt/odlux/apps/eventLogApp/webpack.config.js index de309c1ba..3d056ece1 100644 --- a/sdnr/wt/odlux/apps/eventLogApp/webpack.config.js +++ b/sdnr/wt/odlux/apps/eventLogApp/webpack.config.js @@ -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/apps/faultApp/package.json b/sdnr/wt/odlux/apps/faultApp/package.json index a5958d8c4..953105584 100644 --- a/sdnr/wt/odlux/apps/faultApp/package.json +++ b/sdnr/wt/odlux/apps/faultApp/package.json @@ -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" } } diff --git a/sdnr/wt/odlux/apps/faultApp/pom.xml b/sdnr/wt/odlux/apps/faultApp/pom.xml index 31a376014..ba4df509b 100644 --- a/sdnr/wt/odlux/apps/faultApp/pom.xml +++ b/sdnr/wt/odlux/apps/faultApp/pom.xml @@ -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> @@ -139,7 +140,7 @@ <!-- 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/faultApp/src/actions/clearStuckAlarmsAction.ts b/sdnr/wt/odlux/apps/faultApp/src/actions/clearStuckAlarmsAction.ts index cdcbf64d2..7aac8ba35 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/actions/clearStuckAlarmsAction.ts +++ b/sdnr/wt/odlux/apps/faultApp/src/actions/clearStuckAlarmsAction.ts @@ -17,20 +17,21 @@ */ -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 diff --git a/sdnr/wt/odlux/apps/faultApp/src/actions/panelChangeActions.ts b/sdnr/wt/odlux/apps/faultApp/src/actions/panelChangeActions.ts index 7cf02ac4f..fb29e9c1b 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/actions/panelChangeActions.ts +++ b/sdnr/wt/odlux/apps/faultApp/src/actions/panelChangeActions.ts @@ -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); -} +}; diff --git a/sdnr/wt/odlux/apps/faultApp/src/actions/statusActions.ts b/sdnr/wt/odlux/apps/faultApp/src/actions/statusActions.ts index 54fea6a5f..8b631b96d 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/actions/statusActions.ts +++ b/sdnr/wt/odlux/apps/faultApp/src/actions/statusActions.ts @@ -15,10 +15,11 @@ * 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 index 000000000..aabbf4cf4 --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/src/assets/icons/faultAppIcon.svg @@ -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> diff --git a/sdnr/wt/odlux/apps/faultApp/src/components/clearStuckAlarmsDialog.tsx b/sdnr/wt/odlux/apps/faultApp/src/components/clearStuckAlarmsDialog.tsx index 463c2079c..e86b756a7 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/components/clearStuckAlarmsDialog.tsx +++ b/sdnr/wt/odlux/apps/faultApp/src/components/clearStuckAlarmsDialog.tsx @@ -16,119 +16,122 @@ * ============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); diff --git a/sdnr/wt/odlux/apps/faultApp/src/components/dashboardHome.tsx b/sdnr/wt/odlux/apps/faultApp/src/components/dashboardHome.tsx index a81705965..a3e32c42c 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/components/dashboardHome.tsx +++ b/sdnr/wt/odlux/apps/faultApp/src/components/dashboardHome.tsx @@ -15,26 +15,25 @@ * 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 diff --git a/sdnr/wt/odlux/apps/faultApp/src/components/faultStatus.tsx b/sdnr/wt/odlux/apps/faultApp/src/components/faultStatus.tsx index 7820dfdeb..c2ca4a536 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/components/faultStatus.tsx +++ b/sdnr/wt/odlux/apps/faultApp/src/components/faultStatus.tsx @@ -15,37 +15,36 @@ * 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)); diff --git a/sdnr/wt/odlux/apps/faultApp/src/components/refreshAlarmLogDialog.tsx b/sdnr/wt/odlux/apps/faultApp/src/components/refreshAlarmLogDialog.tsx index 8c639eec9..59657d8de 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/components/refreshAlarmLogDialog.tsx +++ b/sdnr/wt/odlux/apps/faultApp/src/components/refreshAlarmLogDialog.tsx @@ -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 index 000000000..20cd514cd --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/src/components/refreshCurrentAlarmsDialog.tsx @@ -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 index e501ec0ad..000000000 --- a/sdnr/wt/odlux/apps/faultApp/src/components/refreshCurrentProblemsDialog.tsx +++ /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 diff --git a/sdnr/wt/odlux/apps/faultApp/src/handlers/alarmLogEntriesHandler.ts b/sdnr/wt/odlux/apps/faultApp/src/handlers/alarmLogEntriesHandler.ts index 31b8259b2..bdd459669 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/handlers/alarmLogEntriesHandler.ts +++ b/sdnr/wt/odlux/apps/faultApp/src/handlers/alarmLogEntriesHandler.ts @@ -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'; diff --git a/sdnr/wt/odlux/apps/faultApp/src/handlers/clearStuckAlarmsHandler.ts b/sdnr/wt/odlux/apps/faultApp/src/handlers/clearStuckAlarmsHandler.ts index 14634b4c4..0d5a8c70d 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/handlers/clearStuckAlarmsHandler.ts +++ b/sdnr/wt/odlux/apps/faultApp/src/handlers/clearStuckAlarmsHandler.ts @@ -16,21 +16,22 @@ * ============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 diff --git a/sdnr/wt/odlux/apps/faultApp/src/handlers/currentProblemsHandler.ts b/sdnr/wt/odlux/apps/faultApp/src/handlers/currentAlarmsHandler.ts index 3698a2798..70aa1c27e 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/handlers/currentProblemsHandler.ts +++ b/sdnr/wt/odlux/apps/faultApp/src/handlers/currentAlarmsHandler.ts @@ -15,22 +15,22 @@ * 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); diff --git a/sdnr/wt/odlux/apps/faultApp/src/handlers/faultAppRootHandler.ts b/sdnr/wt/odlux/apps/faultApp/src/handlers/faultAppRootHandler.ts index 2ab1da2ed..e4a19ae5c 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/handlers/faultAppRootHandler.ts +++ b/sdnr/wt/odlux/apps/faultApp/src/handlers/faultAppRootHandler.ts @@ -17,22 +17,21 @@ */ // 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, diff --git a/sdnr/wt/odlux/apps/faultApp/src/handlers/faultStatusHandler.ts b/sdnr/wt/odlux/apps/faultApp/src/handlers/faultStatusHandler.ts index 6fa95b685..21b033e6a 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/handlers/faultStatusHandler.ts +++ b/sdnr/wt/odlux/apps/faultApp/src/handlers/faultStatusHandler.ts @@ -15,24 +15,25 @@ * 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 diff --git a/sdnr/wt/odlux/apps/faultApp/src/handlers/notificationsHandler.ts b/sdnr/wt/odlux/apps/faultApp/src/handlers/notificationsHandler.ts index aa92d2a1c..3d960bfc4 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/handlers/notificationsHandler.ts +++ b/sdnr/wt/odlux/apps/faultApp/src/handlers/notificationsHandler.ts @@ -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 diff --git a/sdnr/wt/odlux/apps/faultApp/src/models/fault.ts b/sdnr/wt/odlux/apps/faultApp/src/models/fault.ts index 5f38e5fe9..c70253e58 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/models/fault.ts +++ b/sdnr/wt/odlux/apps/faultApp/src/models/fault.ts @@ -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 diff --git a/sdnr/wt/odlux/apps/faultApp/src/models/panelId.ts b/sdnr/wt/odlux/apps/faultApp/src/models/panelId.ts index 186aa53d9..daebad0e5 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/models/panelId.ts +++ b/sdnr/wt/odlux/apps/faultApp/src/models/panelId.ts @@ -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 diff --git a/sdnr/wt/odlux/apps/faultApp/src/pluginFault.tsx b/sdnr/wt/odlux/apps/faultApp/src/pluginFault.tsx index ff901b097..ca1cfc171 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/pluginFault.tsx +++ b/sdnr/wt/odlux/apps/faultApp/src/pluginFault.tsx @@ -17,92 +17,85 @@ */ // 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; + }); } diff --git a/sdnr/wt/odlux/apps/faultApp/src/services/faultStatusService.ts b/sdnr/wt/odlux/apps/faultApp/src/services/faultStatusService.ts index 880ed7715..0c7a215f8 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/services/faultStatusService.ts +++ b/sdnr/wt/odlux/apps/faultApp/src/services/faultStatusService.ts @@ -15,14 +15,15 @@ * 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 diff --git a/sdnr/wt/odlux/apps/faultApp/src/views/faultApplication.tsx b/sdnr/wt/odlux/apps/faultApp/src/views/faultApplication.tsx index b9cd5e4da..b9f115ed7 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/views/faultApplication.tsx +++ b/sdnr/wt/odlux/apps/faultApp/src/views/faultApplication.tsx @@ -15,44 +15,37 @@ * 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)); diff --git a/sdnr/wt/odlux/apps/faultApp/tsconfig.json b/sdnr/wt/odlux/apps/faultApp/tsconfig.json index a66b5d828..ca65092e0 100644 --- a/sdnr/wt/odlux/apps/faultApp/tsconfig.json +++ b/sdnr/wt/odlux/apps/faultApp/tsconfig.json @@ -4,7 +4,7 @@ "outDir": "./dist", "sourceMap": true, "forceConsistentCasingInFileNames": true, - "allowSyntheticDefaultImports": false, + "allowSyntheticDefaultImports": true, "allowUnreachableCode": false, "allowUnusedLabels": false, "noFallthroughCasesInSwitch": true, diff --git a/sdnr/wt/odlux/apps/faultApp/webpack.config.js b/sdnr/wt/odlux/apps/faultApp/webpack.config.js index d34d31c91..bc26de1cb 100644 --- a/sdnr/wt/odlux/apps/faultApp/webpack.config.js +++ b/sdnr/wt/odlux/apps/faultApp/webpack.config.js @@ -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 diff --git a/sdnr/wt/odlux/apps/helpApp/pom.xml b/sdnr/wt/odlux/apps/helpApp/pom.xml index 9da030913..07ac09164 100644 --- a/sdnr/wt/odlux/apps/helpApp/pom.xml +++ b/sdnr/wt/odlux/apps/helpApp/pom.xml @@ -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> @@ -139,7 +140,7 @@ <!-- 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 index 000000000..298eaa162 --- /dev/null +++ b/sdnr/wt/odlux/apps/helpApp/src/assets/icons/helpAppIcon.svg @@ -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> diff --git a/sdnr/wt/odlux/apps/helpApp/src/components/helpStatus.tsx b/sdnr/wt/odlux/apps/helpApp/src/components/helpStatus.tsx index fd4cd3fa4..985b404a7 100644 --- a/sdnr/wt/odlux/apps/helpApp/src/components/helpStatus.tsx +++ b/sdnr/wt/odlux/apps/helpApp/src/components/helpStatus.tsx @@ -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: { diff --git a/sdnr/wt/odlux/apps/helpApp/src/handlers/helpAppRootHandler.ts b/sdnr/wt/odlux/apps/helpApp/src/handlers/helpAppRootHandler.ts index cc6a98488..29e07959a 100644 --- a/sdnr/wt/odlux/apps/helpApp/src/handlers/helpAppRootHandler.ts +++ b/sdnr/wt/odlux/apps/helpApp/src/handlers/helpAppRootHandler.ts @@ -18,9 +18,10 @@ // 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; diff --git a/sdnr/wt/odlux/apps/helpApp/src/plugin.tsx b/sdnr/wt/odlux/apps/helpApp/src/plugin.tsx index 50a264b15..5d860e530 100644 --- a/sdnr/wt/odlux/apps/helpApp/src/plugin.tsx +++ b/sdnr/wt/odlux/apps/helpApp/src/plugin.tsx @@ -17,14 +17,12 @@ */ // 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); diff --git a/sdnr/wt/odlux/apps/helpApp/src/views/helpApplication.tsx b/sdnr/wt/odlux/apps/helpApp/src/views/helpApplication.tsx index eab44b4ca..5940517b4 100644 --- a/sdnr/wt/odlux/apps/helpApp/src/views/helpApplication.tsx +++ b/sdnr/wt/odlux/apps/helpApp/src/views/helpApplication.tsx @@ -15,13 +15,13 @@ * 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"; diff --git a/sdnr/wt/odlux/apps/helpApp/src/views/helpTocApp.tsx b/sdnr/wt/odlux/apps/helpApp/src/views/helpTocApp.tsx index e1de4d0f5..6a6a89123 100644 --- a/sdnr/wt/odlux/apps/helpApp/src/views/helpTocApp.tsx +++ b/sdnr/wt/odlux/apps/helpApp/src/views/helpTocApp.tsx @@ -16,10 +16,10 @@ * ============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"; diff --git a/sdnr/wt/odlux/apps/helpApp/tsconfig.json b/sdnr/wt/odlux/apps/helpApp/tsconfig.json index a66b5d828..ca65092e0 100644 --- a/sdnr/wt/odlux/apps/helpApp/tsconfig.json +++ b/sdnr/wt/odlux/apps/helpApp/tsconfig.json @@ -4,7 +4,7 @@ "outDir": "./dist", "sourceMap": true, "forceConsistentCasingInFileNames": true, - "allowSyntheticDefaultImports": false, + "allowSyntheticDefaultImports": true, "allowUnreachableCode": false, "allowUnusedLabels": false, "noFallthroughCasesInSwitch": true, diff --git a/sdnr/wt/odlux/apps/helpApp/webpack.config.js b/sdnr/wt/odlux/apps/helpApp/webpack.config.js index b069c2a31..a48f7b976 100644 --- a/sdnr/wt/odlux/apps/helpApp/webpack.config.js +++ b/sdnr/wt/odlux/apps/helpApp/webpack.config.js @@ -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]' + } + }] }] }, diff --git a/sdnr/wt/odlux/apps/inventoryApp/pom.xml b/sdnr/wt/odlux/apps/inventoryApp/pom.xml index e39e26744..c6ec595c0 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/pom.xml +++ b/sdnr/wt/odlux/apps/inventoryApp/pom.xml @@ -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> @@ -139,7 +140,7 @@ <!-- 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 index 000000000..710959a2a --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/src/actions/inventoryDeviceListActions.ts @@ -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)); +}; + diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/actions/inventoryTreeActions.ts b/sdnr/wt/odlux/apps/inventoryApp/src/actions/inventoryTreeActions.ts index c09b669a1..2c6a0ed65 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/actions/inventoryTreeActions.ts +++ b/sdnr/wt/odlux/apps/inventoryApp/src/actions/inventoryTreeActions.ts @@ -16,14 +16,13 @@ * ============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)); } }; diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/actions/panelActions.ts b/sdnr/wt/odlux/apps/inventoryApp/src/actions/panelActions.ts index 10fde8c1d..d66608296 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/actions/panelActions.ts +++ b/sdnr/wt/odlux/apps/inventoryApp/src/actions/panelActions.ts @@ -16,16 +16,16 @@ * ============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 index 000000000..507a835ab --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/src/assets/icons/inventoryAppIcon.svg @@ -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> diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/components/refreshInventoryDialog.tsx b/sdnr/wt/odlux/apps/inventoryApp/src/components/refreshInventoryDialog.tsx index 04658a319..027622249 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/components/refreshInventoryDialog.tsx +++ b/sdnr/wt/odlux/apps/inventoryApp/src/components/refreshInventoryDialog.tsx @@ -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); diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/fakeData/index.ts b/sdnr/wt/odlux/apps/inventoryApp/src/fakeData/index.ts index 46827e842..136b908dd 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/fakeData/index.ts +++ b/sdnr/wt/odlux/apps/inventoryApp/src/fakeData/index.ts @@ -16,11 +16,10 @@ * ============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 index e6138cc38..000000000 --- a/sdnr/wt/odlux/apps/inventoryApp/src/handlers/connectedNetworkElementsHandler.ts +++ /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); diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryAppRootHandler.ts b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryAppRootHandler.ts index 0e857ffe9..b1a0c581f 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryAppRootHandler.ts +++ b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryAppRootHandler.ts @@ -18,27 +18,23 @@ // 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 index 000000000..7c06cad99 --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryDeviceListActionHandler.ts @@ -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 diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryElementsHandler.ts b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryElementsHandler.ts index a65319efa..7bac8f632 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryElementsHandler.ts +++ b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryElementsHandler.ts @@ -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, diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryTreeHandler.ts b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryTreeHandler.ts index 9029a6719..fe90d9820 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryTreeHandler.ts +++ b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryTreeHandler.ts @@ -16,10 +16,10 @@ * ============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 diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/handlers/panelHandler.ts b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/panelHandler.ts index 761253112..7912d0ea5 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/handlers/panelHandler.ts +++ b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/panelHandler.ts @@ -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 diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/models/inventory.ts b/sdnr/wt/odlux/apps/inventoryApp/src/models/inventory.ts index a6c990529..a09fd7e41 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/models/inventory.ts +++ b/sdnr/wt/odlux/apps/inventoryApp/src/models/inventory.ts @@ -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 index 000000000..ab2411401 --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/src/models/inventoryDeviceListType.ts @@ -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; +}; diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/models/networkElementConnection.ts b/sdnr/wt/odlux/apps/inventoryApp/src/models/networkElementConnection.ts index 88f70181c..e1ef1ea2d 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/models/networkElementConnection.ts +++ b/sdnr/wt/odlux/apps/inventoryApp/src/models/networkElementConnection.ts @@ -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; }[]; - } -} + }; +}; diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/models/panelId.ts b/sdnr/wt/odlux/apps/inventoryApp/src/models/panelId.ts index b05c1db64..8f8224c8c 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/models/panelId.ts +++ b/sdnr/wt/odlux/apps/inventoryApp/src/models/panelId.ts @@ -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 diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/pluginInventory.tsx b/sdnr/wt/odlux/apps/inventoryApp/src/pluginInventory.tsx index 50339c0ab..819859919 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/pluginInventory.tsx +++ b/sdnr/wt/odlux/apps/inventoryApp/src/pluginInventory.tsx @@ -16,38 +16,33 @@ * ============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', }); } diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/services/inventoryService.ts b/sdnr/wt/odlux/apps/inventoryApp/src/services/inventoryService.ts index cb70bf522..4014fcf6d 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/services/inventoryService.ts +++ b/sdnr/wt/odlux/apps/inventoryApp/src/services/inventoryService.ts @@ -15,54 +15,76 @@ * 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; } } diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/views/dashboard.tsx b/sdnr/wt/odlux/apps/inventoryApp/src/views/dashboard.tsx index 284f70239..acd2c6216 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/views/dashboard.tsx +++ b/sdnr/wt/odlux/apps/inventoryApp/src/views/dashboard.tsx @@ -16,99 +16,93 @@ * ============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'); } - } } diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/views/detail.tsx b/sdnr/wt/odlux/apps/inventoryApp/src/views/detail.tsx index 252663935..8d47ec3d9 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/views/detail.tsx +++ b/sdnr/wt/odlux/apps/inventoryApp/src/views/detail.tsx @@ -15,20 +15,19 @@ * 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> ))); diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/views/treeview.tsx b/sdnr/wt/odlux/apps/inventoryApp/src/views/treeview.tsx index b0e962daa..954c074c1 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/views/treeview.tsx +++ b/sdnr/wt/odlux/apps/inventoryApp/src/views/treeview.tsx @@ -15,42 +15,38 @@ * 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('*'); } } diff --git a/sdnr/wt/odlux/apps/inventoryApp/tsconfig.json b/sdnr/wt/odlux/apps/inventoryApp/tsconfig.json index a66b5d828..ca65092e0 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/tsconfig.json +++ b/sdnr/wt/odlux/apps/inventoryApp/tsconfig.json @@ -4,7 +4,7 @@ "outDir": "./dist", "sourceMap": true, "forceConsistentCasingInFileNames": true, - "allowSyntheticDefaultImports": false, + "allowSyntheticDefaultImports": true, "allowUnreachableCode": false, "allowUnusedLabels": false, "noFallthroughCasesInSwitch": true, diff --git a/sdnr/wt/odlux/apps/inventoryApp/webpack.config.js b/sdnr/wt/odlux/apps/inventoryApp/webpack.config.js index 403cc53f5..6a780560d 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/webpack.config.js +++ b/sdnr/wt/odlux/apps/inventoryApp/webpack.config.js @@ -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 diff --git a/sdnr/wt/odlux/apps/maintenanceApp/package.json b/sdnr/wt/odlux/apps/maintenanceApp/package.json index dd678d693..d7c325409 100644 --- a/sdnr/wt/odlux/apps/maintenanceApp/package.json +++ b/sdnr/wt/odlux/apps/maintenanceApp/package.json @@ -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" } } diff --git a/sdnr/wt/odlux/apps/maintenanceApp/pom.xml b/sdnr/wt/odlux/apps/maintenanceApp/pom.xml index f70e5fd05..b296a0aa0 100644 --- a/sdnr/wt/odlux/apps/maintenanceApp/pom.xml +++ b/sdnr/wt/odlux/apps/maintenanceApp/pom.xml @@ -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> @@ -139,7 +140,7 @@ <!-- 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/maintenanceApp/src/actions/maintenenceActions.ts b/sdnr/wt/odlux/apps/maintenanceApp/src/actions/maintenenceActions.ts index 162d94367..740abff85 100644 --- a/sdnr/wt/odlux/apps/maintenanceApp/src/actions/maintenenceActions.ts +++ b/sdnr/wt/odlux/apps/maintenanceApp/src/actions/maintenenceActions.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unused-expressions */ /** * ============LICENSE_START======================================================================== * ONAP : ccsdk feature sdnr wt odlux @@ -15,16 +16,13 @@ * 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 index 000000000..8b99a5e7f --- /dev/null +++ b/sdnr/wt/odlux/apps/maintenanceApp/src/assets/icons/maintenanceAppIcon.svg @@ -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> diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src/components/editMaintenenceEntryDialog.tsx b/sdnr/wt/odlux/apps/maintenanceApp/src/components/editMaintenenceEntryDialog.tsx index 829289224..9ab147ca7 100644 --- a/sdnr/wt/odlux/apps/maintenanceApp/src/components/editMaintenenceEntryDialog.tsx +++ b/sdnr/wt/odlux/apps/maintenanceApp/src/components/editMaintenenceEntryDialog.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unused-expressions */ /** * ============LICENSE_START======================================================================== * ONAP : ccsdk feature sdnr wt odlux @@ -18,90 +19,89 @@ 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; diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src/components/refreshMaintenanceEntries.tsx b/sdnr/wt/odlux/apps/maintenanceApp/src/components/refreshMaintenanceEntries.tsx index c66b6f460..e8bd635dd 100644 --- a/sdnr/wt/odlux/apps/maintenanceApp/src/components/refreshMaintenanceEntries.tsx +++ b/sdnr/wt/odlux/apps/maintenanceApp/src/components/refreshMaintenanceEntries.tsx @@ -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); diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src/handlers/maintenanceAppRootHandler.ts b/sdnr/wt/odlux/apps/maintenanceApp/src/handlers/maintenanceAppRootHandler.ts index 71b4bf70d..ced7f2160 100644 --- a/sdnr/wt/odlux/apps/maintenanceApp/src/handlers/maintenanceAppRootHandler.ts +++ b/sdnr/wt/odlux/apps/maintenanceApp/src/handlers/maintenanceAppRootHandler.ts @@ -19,22 +19,20 @@ 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); diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src/handlers/maintenenceEntriesHandler.ts b/sdnr/wt/odlux/apps/maintenanceApp/src/handlers/maintenanceEntriesHandler.ts index 445541889..c3fe51e80 100644 --- a/sdnr/wt/odlux/apps/maintenanceApp/src/handlers/maintenenceEntriesHandler.ts +++ b/sdnr/wt/odlux/apps/maintenanceApp/src/handlers/maintenanceEntriesHandler.ts @@ -15,14 +15,14 @@ * 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); diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src/models/maintenenceEntryType.ts b/sdnr/wt/odlux/apps/maintenanceApp/src/models/maintenanceEntryType.ts index 989bfbdc3..27cdc8c12 100644 --- a/sdnr/wt/odlux/apps/maintenanceApp/src/models/maintenenceEntryType.ts +++ b/sdnr/wt/odlux/apps/maintenanceApp/src/models/maintenanceEntryType.ts @@ -18,16 +18,16 @@ /** 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; -} +}; diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src/pluginMaintenance.tsx b/sdnr/wt/odlux/apps/maintenanceApp/src/pluginMaintenance.tsx index e6ab977b3..0f686cb84 100644 --- a/sdnr/wt/odlux/apps/maintenanceApp/src/pluginMaintenance.tsx +++ b/sdnr/wt/odlux/apps/maintenanceApp/src/pluginMaintenance.tsx @@ -17,27 +17,27 @@ */ // 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', }); } diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src/services/maintenenceService.ts b/sdnr/wt/odlux/apps/maintenanceApp/src/services/maintenenceService.ts index 613b4786d..5fdccc349 100644 --- a/sdnr/wt/odlux/apps/maintenanceApp/src/services/maintenenceService.ts +++ b/sdnr/wt/odlux/apps/maintenanceApp/src/services/maintenenceService.ts @@ -15,15 +15,15 @@ * 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; } } diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src/utils/timeUtils.ts b/sdnr/wt/odlux/apps/maintenanceApp/src/utils/timeUtils.ts index 676be1a38..0fde5fcad 100644 --- a/sdnr/wt/odlux/apps/maintenanceApp/src/utils/timeUtils.ts +++ b/sdnr/wt/odlux/apps/maintenanceApp/src/utils/timeUtils.ts @@ -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}/, '.'); diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src/views/maintenenceView.tsx b/sdnr/wt/odlux/apps/maintenanceApp/src/views/maintenanceView.tsx index d040e3dda..d54d63c04 100644 --- a/sdnr/wt/odlux/apps/maintenanceApp/src/views/maintenenceView.tsx +++ b/sdnr/wt/odlux/apps/maintenanceApp/src/views/maintenanceView.tsx @@ -15,62 +15,57 @@ * 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)); + diff --git a/sdnr/wt/odlux/apps/maintenanceApp/tsconfig.json b/sdnr/wt/odlux/apps/maintenanceApp/tsconfig.json index a66b5d828..ca65092e0 100644 --- a/sdnr/wt/odlux/apps/maintenanceApp/tsconfig.json +++ b/sdnr/wt/odlux/apps/maintenanceApp/tsconfig.json @@ -4,7 +4,7 @@ "outDir": "./dist", "sourceMap": true, "forceConsistentCasingInFileNames": true, - "allowSyntheticDefaultImports": false, + "allowSyntheticDefaultImports": true, "allowUnreachableCode": false, "allowUnusedLabels": false, "noFallthroughCasesInSwitch": true, diff --git a/sdnr/wt/odlux/apps/maintenanceApp/webpack.config.js b/sdnr/wt/odlux/apps/maintenanceApp/webpack.config.js index da5cf5880..845fc43a6 100644 --- a/sdnr/wt/odlux/apps/maintenanceApp/webpack.config.js +++ b/sdnr/wt/odlux/apps/maintenanceApp/webpack.config.js @@ -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/apps/mediatorApp/pom.xml b/sdnr/wt/odlux/apps/mediatorApp/pom.xml index 2f8a147de..2406c6cb3 100644 --- a/sdnr/wt/odlux/apps/mediatorApp/pom.xml +++ b/sdnr/wt/odlux/apps/mediatorApp/pom.xml @@ -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> @@ -139,7 +140,7 @@ <!-- 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 index 000000000..b819aa610 --- /dev/null +++ b/sdnr/wt/odlux/apps/mediatorApp/src/assets/icons/mediatorAppIcon.svg @@ -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> diff --git a/sdnr/wt/odlux/apps/mediatorApp/src/components/showMeditaorInfoDialog.tsx b/sdnr/wt/odlux/apps/mediatorApp/src/components/showMeditaorInfoDialog.tsx index 2b91079b5..f8691ab2f 100644 --- a/sdnr/wt/odlux/apps/mediatorApp/src/components/showMeditaorInfoDialog.tsx +++ b/sdnr/wt/odlux/apps/mediatorApp/src/components/showMeditaorInfoDialog.tsx @@ -16,12 +16,12 @@ * ============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 { diff --git a/sdnr/wt/odlux/apps/mediatorApp/src/plugin.tsx b/sdnr/wt/odlux/apps/mediatorApp/src/plugin.tsx index 5ffd0124b..1c30cfc54 100644 --- a/sdnr/wt/odlux/apps/mediatorApp/src/plugin.tsx +++ b/sdnr/wt/odlux/apps/mediatorApp/src/plugin.tsx @@ -17,14 +17,12 @@ */ // 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) }); diff --git a/sdnr/wt/odlux/apps/mediatorApp/src/views/mediatorApplication.tsx b/sdnr/wt/odlux/apps/mediatorApp/src/views/mediatorApplication.tsx index 55d9b400e..03ce4532e 100644 --- a/sdnr/wt/odlux/apps/mediatorApp/src/views/mediatorApplication.tsx +++ b/sdnr/wt/odlux/apps/mediatorApp/src/views/mediatorApplication.tsx @@ -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'; diff --git a/sdnr/wt/odlux/apps/mediatorApp/src/views/mediatorServerSelection.tsx b/sdnr/wt/odlux/apps/mediatorApp/src/views/mediatorServerSelection.tsx index 456ed6d75..fb12f2608 100644 --- a/sdnr/wt/odlux/apps/mediatorApp/src/views/mediatorServerSelection.tsx +++ b/sdnr/wt/odlux/apps/mediatorApp/src/views/mediatorServerSelection.tsx @@ -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'; diff --git a/sdnr/wt/odlux/apps/mediatorApp/tsconfig.json b/sdnr/wt/odlux/apps/mediatorApp/tsconfig.json index b0c9b424d..c95005660 100644 --- a/sdnr/wt/odlux/apps/mediatorApp/tsconfig.json +++ b/sdnr/wt/odlux/apps/mediatorApp/tsconfig.json @@ -4,7 +4,7 @@ "outDir": "./dist", "sourceMap": true, "forceConsistentCasingInFileNames": true, - "allowSyntheticDefaultImports": false, + "allowSyntheticDefaultImports": true, "allowUnreachableCode": false, "allowUnusedLabels": false, "noFallthroughCasesInSwitch": true, diff --git a/sdnr/wt/odlux/apps/mediatorApp/webpack.config.js b/sdnr/wt/odlux/apps/mediatorApp/webpack.config.js index f626632e2..813c28f37 100644 --- a/sdnr/wt/odlux/apps/mediatorApp/webpack.config.js +++ b/sdnr/wt/odlux/apps/mediatorApp/webpack.config.js @@ -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/apps/minimumApp/pom.xml b/sdnr/wt/odlux/apps/minimumApp/pom.xml index 57b08836b..a58acd313 100644 --- a/sdnr/wt/odlux/apps/minimumApp/pom.xml +++ b/sdnr/wt/odlux/apps/minimumApp/pom.xml @@ -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> @@ -139,7 +140,7 @@ <!-- 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 index 000000000..298eaa162 --- /dev/null +++ b/sdnr/wt/odlux/apps/minimumApp/src/assets/icons/minimumAppIcon.svg @@ -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> diff --git a/sdnr/wt/odlux/apps/minimumApp/src/handlers/minimumAppRootHandler.ts b/sdnr/wt/odlux/apps/minimumApp/src/handlers/minimumAppRootHandler.ts index 463ad21e2..4a4dc613a 100644 --- a/sdnr/wt/odlux/apps/minimumApp/src/handlers/minimumAppRootHandler.ts +++ b/sdnr/wt/odlux/apps/minimumApp/src/handlers/minimumAppRootHandler.ts @@ -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; } } diff --git a/sdnr/wt/odlux/apps/minimumApp/src/plugin.tsx b/sdnr/wt/odlux/apps/minimumApp/src/plugin.tsx index 3095ba783..5c8500a98 100644 --- a/sdnr/wt/odlux/apps/minimumApp/src/plugin.tsx +++ b/sdnr/wt/odlux/apps/minimumApp/src/plugin.tsx @@ -17,19 +17,19 @@ */ // 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', }); } diff --git a/sdnr/wt/odlux/apps/minimumApp/tsconfig.json b/sdnr/wt/odlux/apps/minimumApp/tsconfig.json index a66b5d828..ca65092e0 100644 --- a/sdnr/wt/odlux/apps/minimumApp/tsconfig.json +++ b/sdnr/wt/odlux/apps/minimumApp/tsconfig.json @@ -4,7 +4,7 @@ "outDir": "./dist", "sourceMap": true, "forceConsistentCasingInFileNames": true, - "allowSyntheticDefaultImports": false, + "allowSyntheticDefaultImports": true, "allowUnreachableCode": false, "allowUnusedLabels": false, "noFallthroughCasesInSwitch": true, diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/pom.xml b/sdnr/wt/odlux/apps/performanceHistoryApp/pom.xml index 9c594efb1..78e38c026 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/pom.xml +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/pom.xml @@ -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> @@ -139,7 +140,7 @@ <!-- 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/performanceHistoryApp/src/actions/deviceListActions.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/deviceListActions.ts index 2b8e6e286..fbbf2c5f2 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/deviceListActions.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/deviceListActions.ts @@ -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)); -} +}; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/ltpAction.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/ltpAction.ts index a678ed78c..1c333ab50 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/ltpAction.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/ltpAction.ts @@ -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 diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/reloadAction.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/reloadAction.ts index 7d6ecfe84..6bd54ce96 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/reloadAction.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/reloadAction.ts @@ -16,10 +16,10 @@ * ============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 diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/timeChangeAction.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/timeChangeAction.ts index a069af13c..13214b24b 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/timeChangeAction.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/timeChangeAction.ts @@ -16,6 +16,7 @@ * ============LICENSE_END========================================================================== */ import { Action } from '../../../../framework/src/flux/action'; + import { PmDataInterval } from '../models/performanceDataType'; export class TimeChangeAction extends Action { diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/toggleActions.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/toggleActions.ts index 0efaaae92..7921ea541 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/toggleActions.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/toggleActions.ts @@ -16,24 +16,21 @@ * ============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 index 000000000..982f1eec3 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/assets/icons/performanceHistoryAppIcon.svg @@ -0,0 +1,50 @@ +<!-- 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" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 500 500">
+<g>
+ <g>
+ <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"/>
+ <rect fill="#565656" x="73.092" y="310.635" width="73.089" height="109.632"/>
+ <rect fill="#565656" x="182.728" y="164.452" width="73.085" height="255.814"/>
+ <rect fill="#565656" x="292.362" y="237.541" width="73.083" height="182.726"/>
+ <rect fill="#565656" x="401.994" y="127.907" width="73.091" height="292.36"/>
+ </g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+</svg>
diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/adaptiveModulation.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/adaptiveModulation.tsx index 17b1374b8..5dac0bc14 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/adaptiveModulation.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/adaptiveModulation.tsx @@ -15,37 +15,36 @@ * 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; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/chartFilter.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/chartFilter.tsx index e66e6c1e6..021f74a4f 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/chartFilter.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/chartFilter.tsx @@ -18,57 +18,58 @@ 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 diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/crossPolarDiscrimination.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/crossPolarDiscrimination.tsx index 14cc02d6d..5f925a9ad 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/crossPolarDiscrimination.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/crossPolarDiscrimination.tsx @@ -15,20 +15,19 @@ * 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; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/ltpSelection.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/ltpSelection.tsx index ef6cfc712..bd6333be7 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/ltpSelection.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/ltpSelection.tsx @@ -15,90 +15,91 @@ * 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 diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/performanceData.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/performanceData.tsx index 6a06ea351..fb608aac6 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/performanceData.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/performanceData.tsx @@ -15,36 +15,36 @@ * 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)); diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/receiveLevel.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/receiveLevel.tsx index 8dc92b8ac..125cc6ee6 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/receiveLevel.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/receiveLevel.tsx @@ -15,37 +15,36 @@ * 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)); diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/signalToInterference.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/signalToInterference.tsx index ee7fe34aa..85241fbb1 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/signalToInterference.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/signalToInterference.tsx @@ -15,37 +15,36 @@ * 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)); diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/temperature.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/temperature.tsx index 31e1d363f..d4b823387 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/temperature.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/temperature.tsx @@ -15,38 +15,37 @@ * 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)); diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/toggleContainer.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/toggleContainer.tsx index 8696fe4d6..e883aef7f 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/toggleContainer.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/toggleContainer.tsx @@ -17,52 +17,54 @@ */ 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 diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/transmissionPower.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/transmissionPower.tsx index 97a35da0a..db9a7c077 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/transmissionPower.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/transmissionPower.tsx @@ -15,75 +15,73 @@ * 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)); diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/adaptiveModulationHandler.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/adaptiveModulationHandler.ts index 8857845d2..9baf54529 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/adaptiveModulationHandler.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/adaptiveModulationHandler.ts @@ -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); diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/availableLtpsActionHandler.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/availableLtpsActionHandler.ts index 4605efdb0..f943ef44c 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/availableLtpsActionHandler.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/availableLtpsActionHandler.ts @@ -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, }; } diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/crossPolarDiscriminationHandler.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/crossPolarDiscriminationHandler.ts index 5008dd56d..96cabfa83 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/crossPolarDiscriminationHandler.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/crossPolarDiscriminationHandler.ts @@ -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); diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/deviceListActionHandler.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/deviceListActionHandler.ts index b3c0db446..11e380ad5 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/deviceListActionHandler.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/deviceListActionHandler.ts @@ -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, }; } } diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/performanceDataHandler.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/performanceDataHandler.ts index a7b63a324..664c7cd6c 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/performanceDataHandler.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/performanceDataHandler.ts @@ -15,12 +15,11 @@ * 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); diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/performanceHistoryRootHandler.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/performanceHistoryRootHandler.ts index a05ff3787..c0cee46ba 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/performanceHistoryRootHandler.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/performanceHistoryRootHandler.ts @@ -17,29 +17,29 @@ */ // 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); diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/receiveLevelHandler.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/receiveLevelHandler.ts index 34fd3ebce..78626ebec 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/receiveLevelHandler.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/receiveLevelHandler.ts @@ -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); diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/signalToInterferenceHandler.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/signalToInterferenceHandler.ts index 5fd5c0cde..ab6efabae 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/signalToInterferenceHandler.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/signalToInterferenceHandler.ts @@ -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); diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/temperatureHandler.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/temperatureHandler.ts index 130b81801..02bf69be7 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/temperatureHandler.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/temperatureHandler.ts @@ -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); diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/transmissionPowerHandler.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/transmissionPowerHandler.ts index 2a09e58f3..9cf70dc87 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/transmissionPowerHandler.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/transmissionPowerHandler.ts @@ -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); diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/availableLtps.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/availableLtps.ts index dc6c7bc16..60061571c 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/availableLtps.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/availableLtps.ts @@ -15,7 +15,7 @@ * the License. * ============LICENSE_END========================================================================== */ - export type LtpIds = { - key : string - } +export type LtpIds = { + key: string; +}; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/chartTypes.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/chartTypes.ts index 53039faa2..969c0b399 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/chartTypes.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/chartTypes.ts @@ -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[]; } /** diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/crossPolarDiscriminationDataType.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/crossPolarDiscriminationDataType.ts index 8adb16f45..749624b9a 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/crossPolarDiscriminationDataType.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/crossPolarDiscriminationDataType.ts @@ -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; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/deviceListType.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/deviceListType.ts index db8f2d71e..fbe314162 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/deviceListType.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/deviceListType.ts @@ -22,4 +22,4 @@ export type DeviceListType = { nodeId: string; -} +}; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/panelId.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/panelId.ts index 5889a64c8..08bf7f815 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/panelId.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/panelId.ts @@ -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 diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/performanceDataType.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/performanceDataType.ts index 30f97fb54..f71e09de9 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/performanceDataType.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/performanceDataType.ts @@ -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 diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/temperatureDataType.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/temperatureDataType.ts index 3b0cb7683..5798d5c5d 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/temperatureDataType.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/temperatureDataType.ts @@ -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; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/toggleDataType.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/toggleDataType.ts index f705e10b2..0e71c9474 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/toggleDataType.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/toggleDataType.ts @@ -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'; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/topologyNetconf.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/topologyNetconf.ts index 99123f55e..f52af9784 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/topologyNetconf.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/topologyNetconf.ts @@ -17,10 +17,10 @@ */ export interface TopologyNode { - "node-id": string; + 'node-id': string; } export interface Topology { - "topology-id": string; + 'topology-id': string; node: TopologyNode[]; } diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/pluginPerformance.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/pluginPerformance.tsx index a8aaca22f..ef939fdba 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/pluginPerformance.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/pluginPerformance.tsx @@ -16,45 +16,64 @@ * ============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', }); } diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/services/performanceHistoryService.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/services/performanceHistoryService.ts index 70a8771b7..ef013f1cb 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/services/performanceHistoryService.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/services/performanceHistoryService.ts @@ -15,13 +15,12 @@ * 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; } } diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/chartUtils.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/chartUtils.tsx index f58638e30..38abb3e79 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/chartUtils.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/chartUtils.tsx @@ -15,15 +15,17 @@ * 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 diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/tableUtils.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/tableUtils.ts index b5a3a3f36..37fe962dc 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/tableUtils.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/tableUtils.ts @@ -15,22 +15,22 @@ * 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 diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/views/performanceHistoryApplication.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/views/performanceHistoryApplication.tsx index b33b442d3..a4b968622 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/views/performanceHistoryApplication.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/views/performanceHistoryApplication.tsx @@ -15,57 +15,54 @@ * 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)); diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/tsconfig.json b/sdnr/wt/odlux/apps/performanceHistoryApp/tsconfig.json index a66b5d828..ca65092e0 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/tsconfig.json +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/tsconfig.json @@ -4,7 +4,7 @@ "outDir": "./dist", "sourceMap": true, "forceConsistentCasingInFileNames": true, - "allowSyntheticDefaultImports": false, + "allowSyntheticDefaultImports": true, "allowUnreachableCode": false, "allowUnusedLabels": false, "noFallthroughCasesInSwitch": true, diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/webpack.config.js b/sdnr/wt/odlux/apps/performanceHistoryApp/webpack.config.js index 8f2192f12..2f25d0df1 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/webpack.config.js +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/webpack.config.js @@ -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 index 000000000..f98e65bcd --- /dev/null +++ b/sdnr/wt/odlux/eslintrc.json @@ -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 diff --git a/sdnr/wt/odlux/framework/pom.xml b/sdnr/wt/odlux/framework/pom.xml index 414f49827..3dd0f1a9d 100644 --- a/sdnr/wt/odlux/framework/pom.xml +++ b/sdnr/wt/odlux/framework/pom.xml @@ -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> @@ -102,7 +103,7 @@ <!-- 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/framework/src/actions/settingsAction.ts b/sdnr/wt/odlux/framework/src/actions/settingsAction.ts index 1c18ddc8e..092b31814 100644 --- a/sdnr/wt/odlux/framework/src/actions/settingsAction.ts +++ b/sdnr/wt/odlux/framework/src/actions/settingsAction.ts @@ -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!)) diff --git a/sdnr/wt/odlux/framework/src/app.tsx b/sdnr/wt/odlux/framework/src/app.tsx index 9b03a216d..bbe1f9ec8 100644 --- a/sdnr/wt/odlux/framework/src/app.tsx +++ b/sdnr/wt/odlux/framework/src/app.tsx @@ -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 index 000000000..156e36efe --- /dev/null +++ b/sdnr/wt/odlux/framework/src/assets/icons/About.svg @@ -0,0 +1,18 @@ +<!-- 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 24 24">
+
+<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 "/>
+
+<path fill="#36A9E1" d="M 12 11 A 1 1 0 1 0 11 10 A 1 1 0 0 0 12 11 Z "/>
+
+<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"/>
+
+</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 index 000000000..0836714b4 --- /dev/null +++ b/sdnr/wt/odlux/framework/src/assets/icons/Home.svg @@ -0,0 +1,28 @@ +<!-- 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" 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;"
+ xml:space="preserve">
+<g>
+ <g>
+ <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
+ 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
+ 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
+ z"/>
+ <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
+ 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
+ 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
+ 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
+ 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
+ c1.522-1.906,2.189-4.145,1.991-6.716C460.068,229.007,459.021,226.961,457.122,225.438z"/>
+
+<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"/>
+ </g>
+</g>
+</svg>
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 index 000000000..ea0312802 --- /dev/null +++ b/sdnr/wt/odlux/framework/src/assets/icons/Menu.svg @@ -0,0 +1,18 @@ +<!-- highstreet technologies GmbH colour scheme
+ Grey #565656
+ LBlue #36A9E1
+ DBlue #246DA2
+ Green #003F2C / #006C4B
+ Yellw #C8D400
+ Red #D81036
+-->
+
+<svg viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+
+<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"/>
+
+<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"/>
+
+<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"/>
+
+</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 index 000000000..1595cdc1c --- /dev/null +++ b/sdnr/wt/odlux/framework/src/assets/icons/Tools.svg @@ -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 index 000000000..99618cf57 --- /dev/null +++ b/sdnr/wt/odlux/framework/src/assets/icons/User.svg @@ -0,0 +1,21 @@ +<!-- 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" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 600 600">
+
+ <defs>
+ <clipPath id="circular-border">
+ <circle cx="300" cy="300" r="250" />
+ </clipPath>
+ </defs>
+
+ <circle fill="#36A9E1" cx="300" cy="300" r="280"/>
+ <circle fill="#ffffff" cx="300" cy="230" r="100" />
+ <circle fill="#ffffff" cx="300" cy="550" r="190" clip-path="url(#circular-border)" />
+</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 Binary files differnew file mode 100644 index 000000000..412390c79 --- /dev/null +++ b/sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.png 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 index 000000000..c1d74bc96 --- /dev/null +++ b/sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.svg @@ -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> diff --git a/sdnr/wt/odlux/framework/src/components/errorDisplay.tsx b/sdnr/wt/odlux/framework/src/components/errorDisplay.tsx index a04ab16af..d41d82687 100644 --- a/sdnr/wt/odlux/framework/src/components/errorDisplay.tsx +++ b/sdnr/wt/odlux/framework/src/components/errorDisplay.tsx @@ -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 index 000000000..0d7d734c9 --- /dev/null +++ b/sdnr/wt/odlux/framework/src/components/icons/menuIcon.tsx @@ -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 diff --git a/sdnr/wt/odlux/framework/src/components/logo.tsx b/sdnr/wt/odlux/framework/src/components/logo.tsx index b10cc8ce1..f2bb1f575 100644 --- a/sdnr/wt/odlux/framework/src/components/logo.tsx +++ b/sdnr/wt/odlux/framework/src/components/logo.tsx @@ -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('')); } diff --git a/sdnr/wt/odlux/framework/src/components/material-table/index.tsx b/sdnr/wt/odlux/framework/src/components/material-table/index.tsx index 8541cfe56..c1a5005d4 100644 --- a/sdnr/wt/odlux/framework/src/components/material-table/index.tsx +++ b/sdnr/wt/odlux/framework/src/components/material-table/index.tsx @@ -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()); } diff --git a/sdnr/wt/odlux/framework/src/components/material-table/showColumnDialog.tsx b/sdnr/wt/odlux/framework/src/components/material-table/showColumnDialog.tsx index f8ae6ea97..ab0d465e7 100644 --- a/sdnr/wt/odlux/framework/src/components/material-table/showColumnDialog.tsx +++ b/sdnr/wt/odlux/framework/src/components/material-table/showColumnDialog.tsx @@ -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'; diff --git a/sdnr/wt/odlux/framework/src/components/material-table/utilities.ts b/sdnr/wt/odlux/framework/src/components/material-table/utilities.ts index f9015493f..e2fda7647 100644 --- a/sdnr/wt/odlux/framework/src/components/material-table/utilities.ts +++ b/sdnr/wt/odlux/framework/src/components/material-table/utilities.ts @@ -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: }; }; diff --git a/sdnr/wt/odlux/framework/src/components/material-ui/listItemLink.tsx b/sdnr/wt/odlux/framework/src/components/material-ui/listItemLink.tsx index 744cb0d24..626cb8978 100644 --- a/sdnr/wt/odlux/framework/src/components/material-ui/listItemLink.tsx +++ b/sdnr/wt/odlux/framework/src/components/material-ui/listItemLink.tsx @@ -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' diff --git a/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx b/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx index 195706d28..d969286b7 100644 --- a/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx +++ b/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx @@ -15,15 +15,14 @@ * 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 } diff --git a/sdnr/wt/odlux/framework/src/components/routing/appFrame.tsx b/sdnr/wt/odlux/framework/src/components/routing/appFrame.tsx index d055b8a87..aa22f17f4 100644 --- a/sdnr/wt/odlux/framework/src/components/routing/appFrame.tsx +++ b/sdnr/wt/odlux/framework/src/components/routing/appFrame.tsx @@ -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'; diff --git a/sdnr/wt/odlux/framework/src/components/settings/general.tsx b/sdnr/wt/odlux/framework/src/components/settings/general.tsx index 90f15c1d2..ffd516ba1 100644 --- a/sdnr/wt/odlux/framework/src/components/settings/general.tsx +++ b/sdnr/wt/odlux/framework/src/components/settings/general.tsx @@ -16,11 +16,11 @@ * ============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'; diff --git a/sdnr/wt/odlux/framework/src/components/titleBar.tsx b/sdnr/wt/odlux/framework/src/components/titleBar.tsx index 19d3bdf74..40c0fc736 100644 --- a/sdnr/wt/odlux/framework/src/components/titleBar.tsx +++ b/sdnr/wt/odlux/framework/src/components/titleBar.tsx @@ -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 ( diff --git a/sdnr/wt/odlux/framework/src/flux/connect.ts b/sdnr/wt/odlux/framework/src/flux/connect.tsx index f54e4e0f0..09d30dae7 100644 --- a/sdnr/wt/odlux/framework/src/flux/connect.ts +++ b/sdnr/wt/odlux/framework/src/flux/connect.tsx @@ -15,13 +15,14 @@ * 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; +}; diff --git a/sdnr/wt/odlux/framework/src/flux/store.ts b/sdnr/wt/odlux/framework/src/flux/store.ts index dd80ce7ba..347d295e0 100644 --- a/sdnr/wt/odlux/framework/src/flux/store.ts +++ b/sdnr/wt/odlux/framework/src/flux/store.ts @@ -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(); } diff --git a/sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts b/sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts index f98d77487..71b9e33d1 100644 --- a/sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts +++ b/sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts @@ -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; diff --git a/sdnr/wt/odlux/framework/src/middleware/logger.ts b/sdnr/wt/odlux/framework/src/middleware/logger.ts index 47725e696..fb0874f3e 100644 --- a/sdnr/wt/odlux/framework/src/middleware/logger.ts +++ b/sdnr/wt/odlux/framework/src/middleware/logger.ts @@ -18,16 +18,17 @@ 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(); diff --git a/sdnr/wt/odlux/framework/src/middleware/navigation.ts b/sdnr/wt/odlux/framework/src/middleware/navigation.ts index 5f3eed55d..44035fec3 100644 --- a/sdnr/wt/odlux/framework/src/middleware/navigation.ts +++ b/sdnr/wt/odlux/framework/src/middleware/navigation.ts @@ -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 diff --git a/sdnr/wt/odlux/framework/src/services/applicationApi.ts b/sdnr/wt/odlux/framework/src/services/applicationApi.ts index 36523f9eb..8246ee8fa 100644 --- a/sdnr/wt/odlux/framework/src/services/applicationApi.ts +++ b/sdnr/wt/odlux/framework/src/services/applicationApi.ts @@ -15,13 +15,10 @@ * 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; diff --git a/sdnr/wt/odlux/framework/src/services/applicationManager.ts b/sdnr/wt/odlux/framework/src/services/applicationManager.ts index 14e3ed0b2..bd620a020 100644 --- a/sdnr/wt/odlux/framework/src/services/applicationManager.ts +++ b/sdnr/wt/odlux/framework/src/services/applicationManager.ts @@ -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. */ diff --git a/sdnr/wt/odlux/framework/src/services/broadcastService.ts b/sdnr/wt/odlux/framework/src/services/broadcastService.ts index f2c3ebc57..40968e54f 100644 --- a/sdnr/wt/odlux/framework/src/services/broadcastService.ts +++ b/sdnr/wt/odlux/framework/src/services/broadcastService.ts @@ -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); + } +}; diff --git a/sdnr/wt/odlux/framework/src/services/index.ts b/sdnr/wt/odlux/framework/src/services/index.ts index 19b451345..85f0708a6 100644 --- a/sdnr/wt/odlux/framework/src/services/index.ts +++ b/sdnr/wt/odlux/framework/src/services/index.ts @@ -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'; diff --git a/sdnr/wt/odlux/framework/src/services/restService.ts b/sdnr/wt/odlux/framework/src/services/restService.ts index d727e4c9e..a296c52a4 100644 --- a/sdnr/wt/odlux/framework/src/services/restService.ts +++ b/sdnr/wt/odlux/framework/src/services/restService.ts @@ -16,18 +16,13 @@ * ============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 index 000000000..cbb5987de --- /dev/null +++ b/sdnr/wt/odlux/framework/src/services/storeService.ts @@ -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 diff --git a/sdnr/wt/odlux/framework/src/services/settingsService.ts b/sdnr/wt/odlux/framework/src/services/userdataService.ts index 6633a794d..5c9b576c3 100644 --- a/sdnr/wt/odlux/framework/src/services/settingsService.ts +++ b/sdnr/wt/odlux/framework/src/services/userdataService.ts @@ -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; diff --git a/sdnr/wt/odlux/framework/src/store/applicationStore.ts b/sdnr/wt/odlux/framework/src/store/applicationStore.ts index a4545eff9..cbe8c20da 100644 --- a/sdnr/wt/odlux/framework/src/store/applicationStore.ts +++ b/sdnr/wt/odlux/framework/src/store/applicationStore.ts @@ -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 index 000000000..a198d98a9 --- /dev/null +++ b/sdnr/wt/odlux/framework/src/utilities/logLevel.ts @@ -0,0 +1,8 @@ +export enum LogLevel { + Always = 0, + Error = 1, + Warning = 2, + Info = 3, + Debug = 4, + Trace = 5, +} diff --git a/sdnr/wt/odlux/framework/src/views/about.tsx b/sdnr/wt/odlux/framework/src/views/about.tsx index ac219708d..937e74f33 100644 --- a/sdnr/wt/odlux/framework/src/views/about.tsx +++ b/sdnr/wt/odlux/framework/src/views/about.tsx @@ -15,22 +15,18 @@ * 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; diff --git a/sdnr/wt/odlux/framework/src/views/frame.tsx b/sdnr/wt/odlux/framework/src/views/frame.tsx index 4676f5ac2..4a93cf0ae 100644 --- a/sdnr/wt/odlux/framework/src/views/frame.tsx +++ b/sdnr/wt/odlux/framework/src/views/frame.tsx @@ -15,15 +15,11 @@ * 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; diff --git a/sdnr/wt/odlux/framework/src/views/home.tsx b/sdnr/wt/odlux/framework/src/views/home.tsx index 92fd0b262..72c5059e1 100644 --- a/sdnr/wt/odlux/framework/src/views/home.tsx +++ b/sdnr/wt/odlux/framework/src/views/home.tsx @@ -16,50 +16,39 @@ * ============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 diff --git a/sdnr/wt/odlux/framework/src/views/login.tsx b/sdnr/wt/odlux/framework/src/views/login.tsx index e037edf82..46c0872bc 100644 --- a/sdnr/wt/odlux/framework/src/views/login.tsx +++ b/sdnr/wt/odlux/framework/src/views/login.tsx @@ -15,39 +15,34 @@ * 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 diff --git a/sdnr/wt/odlux/framework/src/views/settings.tsx b/sdnr/wt/odlux/framework/src/views/settings.tsx index a6b940bfa..5973db9a0 100644 --- a/sdnr/wt/odlux/framework/src/views/settings.tsx +++ b/sdnr/wt/odlux/framework/src/views/settings.tsx @@ -16,30 +16,18 @@ * ============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 diff --git a/sdnr/wt/odlux/framework/src2/main/resources/version.json b/sdnr/wt/odlux/framework/src2/main/resources/version.json index f9e974672..1d9af9029 100644 --- a/sdnr/wt/odlux/framework/src2/main/resources/version.json +++ b/sdnr/wt/odlux/framework/src2/main/resources/version.json @@ -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##" } diff --git a/sdnr/wt/odlux/installer/pom.xml b/sdnr/wt/odlux/installer/pom.xml index 33aa48d89..795f1f1ad 100644 --- a/sdnr/wt/odlux/installer/pom.xml +++ b/sdnr/wt/odlux/installer/pom.xml @@ -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> diff --git a/sdnr/wt/odlux/odlux.properties b/sdnr/wt/odlux/odlux.properties index 8a49bde43..c356533eb 100644 --- a/sdnr/wt/odlux/odlux.properties +++ b/sdnr/wt/odlux/odlux.properties @@ -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) diff --git a/sdnr/wt/odlux/package.json b/sdnr/wt/odlux/package.json index d65555e45..ac9bfb5b4 100644 --- a/sdnr/wt/odlux/package.json +++ b/sdnr/wt/odlux/package.json @@ -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": { @@ -51,24 +52,29 @@ "@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", diff --git a/sdnr/wt/odlux/pom.xml b/sdnr/wt/odlux/pom.xml index 2d91a4ba6..6897cd4d5 100644 --- a/sdnr/wt/odlux/pom.xml +++ b/sdnr/wt/odlux/pom.xml @@ -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 index 000000000..e5b91b60a --- /dev/null +++ b/sdnr/wt/odlux/proxy.conf.js @@ -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 + }, +} + diff --git a/sdnr/wt/odlux/yarn.lock b/sdnr/wt/odlux/yarn.lock index 243411d0d..3c87e837b 100644 --- a/sdnr/wt/odlux/yarn.lock +++ b/sdnr/wt/odlux/yarn.lock @@ -646,6 +646,14 @@ "@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" @@ -653,6 +661,13 @@ 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" @@ -846,6 +861,21 @@ 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" @@ -965,6 +995,25 @@ 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" @@ -1844,11 +1893,32 @@ 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" @@ -2050,6 +2120,16 @@ 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" @@ -2170,6 +2250,11 @@ 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" @@ -2177,6 +2262,89 @@ 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-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== |