From 05ef023752abdb4f1e072332496dc7c6eaff8965 Mon Sep 17 00:00:00 2001 From: herbert Date: Sat, 1 Feb 2020 16:00:00 +0100 Subject: SDN-R add updated odlux Updates all odlux framework and app components. Issue-ID: SDNC-1032 Signed-off-by: herbert Change-Id: I13c520489fd40d05b7fd5215f5941af6238e9cae --- .../src/views/configurationApplication.tsx | 412 ++++++++++++++------- .../src/views/networkElementSelector.tsx | 20 +- 2 files changed, 307 insertions(+), 125 deletions(-) (limited to 'sdnr/wt/odlux/apps/configurationApp/src/views') diff --git a/sdnr/wt/odlux/apps/configurationApp/src/views/configurationApplication.tsx b/sdnr/wt/odlux/apps/configurationApp/src/views/configurationApplication.tsx index 24a4af8b2..9c92ceb6b 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/views/configurationApplication.tsx +++ b/sdnr/wt/odlux/apps/configurationApp/src/views/configurationApplication.tsx @@ -16,7 +16,7 @@ * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import React from 'react'; import { RouteComponentProps, withRouter } from 'react-router-dom'; import { WithStyles, withStyles, createStyles, Theme } from '@material-ui/core/styles'; @@ -26,11 +26,12 @@ import { IApplicationStoreState } from "../../../../framework/src/store/applicat import MaterialTable, { ColumnModel, ColumnType, MaterialTableCtorType } from "../../../../framework/src/components/material-table"; import { Loader } from "../../../../framework/src/components/material-ui/loader"; -import { SetSelectedValue, splitVPath, updateDataActionAsyncCreator } from "../actions/deviceActions"; -import { ViewSpecification, isViewElementString, isViewElementNumber, isViewElementBoolean, isViewElementObjectOrList, isViewElementSelection } from "../models/uiModels"; +import { SetSelectedValue, splitVPath, updateDataActionAsyncCreator, updateViewActionAsyncCreator } from "../actions/deviceActions"; +import { ViewSpecification, isViewElementString, isViewElementNumber, isViewElementBoolean, isViewElementObjectOrList, isViewElementSelection, isViewElementChoise, ViewElement, ViewElementChoise, isViewElementUnion } from "../models/uiModels"; import Fab from '@material-ui/core/Fab'; import AddIcon from '@material-ui/icons/Add'; +import ArrowBack from '@material-ui/icons/ArrowBack'; import RemoveIcon from '@material-ui/icons/RemoveCircleOutline'; import SaveIcon from '@material-ui/icons/Save'; import EditIcon from '@material-ui/icons/Edit'; @@ -38,7 +39,7 @@ import Tooltip from "@material-ui/core/Tooltip"; import TextField from "@material-ui/core/TextField"; import FormControl from "@material-ui/core/FormControl"; import IconButton from "@material-ui/core/IconButton"; -import Button from "@material-ui/core/Button"; + import InputAdornment from "@material-ui/core/InputAdornment"; import InputLabel from "@material-ui/core/InputLabel"; import Select from "@material-ui/core/Select"; @@ -47,6 +48,13 @@ import Breadcrumbs from "@material-ui/core/Breadcrumbs"; import Link from "@material-ui/core/Link"; import FormHelperText from '@material-ui/core/FormHelperText'; +import { UIElementReference } from '../components/uiElementReference'; +import { UiElementNumber } from '../components/uiElementNumber'; +import { UiElementString } from '../components/uiElementString'; +import { UiElementBoolean } from '../components/uiElementBoolean'; +import { UiElementSelection } from '../components/uiElementSelection'; +import { UIElementUnion } from '../components/uiElementUnion'; + const styles = (theme: Theme) => createStyles({ header: { "display": "flex", @@ -97,6 +105,16 @@ const styles = (theme: Theme) => createStyles({ }, }, }, + section: { + padding: "15px", + borderBottom: `2px solid ${theme.palette.divider}`, + }, + viewElements: { + width: 485, marginLeft: 20, marginRight: 20 + }, + verificationElements: { + width: 485, marginLeft: 20, marginRight: 20 + } }); const mapProps = (state: IApplicationStoreState) => ({ @@ -115,6 +133,7 @@ const mapProps = (state: IApplicationStoreState) => ({ const mapDispatch = (dispatcher: IDispatcher) => ({ onValueSelected: (value: any) => dispatcher.dispatch(new SetSelectedValue(value)), onUpdateData: (vPath: string, data: any) => dispatcher.dispatch(updateDataActionAsyncCreator(vPath, data)), + reloadView: (vPath: string) => dispatcher.dispatch(updateViewActionAsyncCreator(vPath)), }); const SelectElementTable = MaterialTable as MaterialTableCtorType<{ [key: string]: any }>; @@ -126,6 +145,7 @@ type ConfigurationApplicationComponentState = { editMode: boolean; canEdit: boolean; viewData: { [key: string]: any } | null; + choises: { [path: string]: { selectedCase: string, data: { [property: string]: any } } }; } const OldProps = Symbol("OldProps"); @@ -134,27 +154,61 @@ class ConfigurationApplicationComponent extends React.Component { + const elm = nextProps.viewSpecification.elements[cur]; + if (isViewElementChoise(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 + const selectedCase = caseKeys.find(key => { + const caseElm = elm.cases[key]; + return Object.keys(caseElm.elements).some(caseElmKey => { + const caseElmElm = caseElm.elements[caseElmKey]; + return nextProps.viewData[caseElmElm.label] != null || nextProps.viewData[caseElmElm.id] != null; + }); + }) || caseKeys[0]; + + // extract all data of the active case + const caseElements = elm.cases[selectedCase].elements; + const data = Object.keys(caseElements).reduce((dataAcc, dataCur) => { + const dataElm = caseElements[dataCur]; + if (nextProps.viewData[dataElm.label] !== undefined) { + dataAcc[dataElm.label] = nextProps.viewData[dataElm.label]; + } else if (nextProps.viewData[dataElm.id] !== undefined) { + dataAcc[dataElm.id] = nextProps.viewData[dataElm.id]; + } + return dataAcc; + }, {} as { [name: string]: any }); + + acc[elm.id] = { + selectedCase, + data, + }; + } + return acc; + }, {} as { [path: string]: { selectedCase: string, data: { [property: string]: any } } }) || {} } return state; } @@ -174,123 +228,203 @@ class ConfigurationApplicationComponent extends React.Component { - const elements = viewSpecification.elements; - return ( - Object.keys(elements).sort((a, b) => { - const vsA = elements[a]; - const vsB = elements[b]; - if (keyProperty) { - // if (vsA.label === vsB.label) return 0; - if (vsA.label === keyProperty) return -1; - if (vsB.label === keyProperty) return +1; - } + private renderUIElement = (uiElement: ViewElement, viewData: { [key: string]: any }, keyProperty: string | undefined, editMode: boolean, isNew: boolean) => { + const isKey = (uiElement.label === keyProperty); + const canEdit = editMode && (isNew || (uiElement.config && !isKey)); + if (isViewElementSelection(uiElement)) { + + return { this.changeValueFor(uiElement.id, e) }} + /> + + } else if (isViewElementBoolean(uiElement)) { + return { this.changeValueFor(uiElement.id, e) }} /> + + } else if (isViewElementString(uiElement)) { + return { this.changeValueFor(uiElement.id, e) }} /> + + } else if (isViewElementNumber(uiElement)) { + return { this.changeValueFor(uiElement.id, e) }} /> + } else if (isViewElementUnion(uiElement)) { + return { this.changeValueFor(uiElement.id, e) }} /> + } + else { + if (process.env.NODE_ENV !== "production") { + console.error(`Unknown element type - ${(uiElement as any).uiType} in ${(uiElement as any).id}.`) + } + return null; + } + }; - if (vsA.uiType === vsB.uiType) return 0; - if (vsA.uiType !== "object" && vsB.uiType !== "object") return 0; - if (vsA.uiType === "object") return +1; - return -1; - }).map(key => { - const uiElement = elements[key]; - const isKey = (uiElement.label === keyProperty); - const canEdit = editMode && (isNew || (uiElement.config && !isKey)); - if (isViewElementSelection(uiElement)) { - let error = "" - const value = String(viewData[uiElement.id]).toLowerCase(); - if (uiElement.mandatory && !!value) { - error = "Error"; - } - return (canEdit || viewData[uiElement.id] != null - ? ( - {uiElement.label} - - {error} - ) - : null - ); - } else if (isViewElementBoolean(uiElement)) { - let error = "" - const value = String(viewData[uiElement.id]).toLowerCase(); - if (uiElement.mandatory && value !== "true" && value !== "false") { - error = "Error"; - } - return (canEdit || viewData[uiElement.id] != null - ? ( - {uiElement.label} - - {error} - ) - : null - ); - } else if (isViewElementString(uiElement)) { - return ( - - { this.changeValueFor(uiElement.id, e.target.value) }} - /> - - ); - } else if (isViewElementNumber(uiElement)) { - return ( - - {uiElement.units} : undefined }} spellCheck={false} autoFocus margin="dense" - id={uiElement.id} label={uiElement.label} type="text" value={viewData[uiElement.id] == null ? '' : viewData[uiElement.id]} - style={{ width: 485, marginLeft: 20, marginRight: 20 }} - onChange={(e) => { this.changeValueFor(uiElement.id, e.target.value) }} - /> - - ); - } else if (isViewElementObjectOrList(uiElement)) { - return ( - - - - - - ); - } else { - if (process.env.NODE_ENV !== "production") { - console.error(`Unknown type - ${(uiElement as any).uiType} in ${(uiElement as any).id}.`) + // private renderUIReference = (uiElement: ViewElement, viewData: { [key: string]: any }, keyProperty: string | undefined, editMode: boolean, isNew: boolean) => { + // const isKey = (uiElement.label === keyProperty); + // const canEdit = editMode && (isNew || (uiElement.config && !isKey)); + // if (isViewElementObjectOrList(uiElement)) { + // return ( + // + // + // + // + // + // ); + // } else { + // if (process.env.NODE_ENV !== "production") { + // console.error(`Unknown reference type - ${(uiElement as any).uiType} in ${(uiElement as any).id}.`) + // } + // return null; + // } + // }; + + private renderUIChoise = (uiElement: ViewElementChoise, 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 canEdit = editMode && (isNew || (uiElement.config && !isKey)); + if (isViewElementChoise(uiElement)) { + const subElements = currentCase ?.elements; + return ( + <> + + {uiElement.label} + + + {subElements + ? Object.keys(subElements).map(elmKey => { + const elm = subElements[elmKey]; + return this.renderUIElement(elm, viewData, keyProperty, editMode, isNew); + }) + :

Invalid Choise

} - return null; + + ); + } else { + if (process.env.NODE_ENV !== "production") { + console.error(`Unknown type - ${(uiElement as any).uiType} in ${(uiElement as any).id}.`) + } + return null; + } + }; + + private renderUIView = (viewSpecification: ViewSpecification, 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; + if (vsA.label === keyProperty) return -1; + if (vsB.label === keyProperty) return +1; + } + + // if (vsA.uiType === vsB.uiType) return 0; + // if (vsA.uiType !== "object" && vsB.uiType !== "object") return 0; + // if (vsA.uiType === "object") return +1; + return -1; + }; + + const sections = Object.keys(viewSpecification.elements).reduce((acc, cur) => { + const elm = viewSpecification.elements[cur]; + if (isViewElementObjectOrList(elm)) { + acc.references.push(elm); + } else if (isViewElementChoise(elm)) { + acc.choises.push(elm); + } else { + acc.elements.push(elm); + } + return acc; + }, { elements: [] as ViewElement[], references: [] as ViewElement[], choises: [] as ViewElementChoise[] }); + + sections.elements = sections.elements.sort(orderFunc); + + return ( + <> +
+ {sections.elements.length > 0 + ? ( +
+ {sections.elements.map(element => this.renderUIElement(element, viewData, keyProperty, editMode, isNew))} +
+ ) : null + } + {sections.references.length > 0 + ? ( +
+ {sections.references.map(element => ( + { this.navigate(`/${elm.id}`) }} /> + ))} +
+ ) : null } - }) + {sections.choises.length > 0 + ? ( +
+ {sections.choises.map(element => this.renderUIChoise(element, viewData, keyProperty, editMode, isNew))} +
+ ) : null + } + ); }; - private renderUIElementList(listSpecification: ViewSpecification, listKeyProperty: string, listData: { [key: string]: any }[]) { + private renderUIViewList(listSpecification: ViewSpecification, listKeyProperty: string, listData: { [key: string]: any }[]) { const listElements = listSpecification.elements; const navigate = (path: string) => { @@ -381,11 +515,41 @@ class ConfigurationApplicationComponent extends React.Component
+ {this.state.editMode && ( + { + this.props.vPath && await this.props.reloadView(this.props.vPath); + this.setState({ editMode: false }); + }} > + ) || null} { /* do not show edit if this is a list or it can't be edited */ !displayAsList && viewSpecification.canEdit && (
{ if (this.state.editMode) { - this.props.onUpdateData(this.props.vPath!, this.state.viewData); + + // ensure only active choises will be contained + const choiseKeys = Object.keys(viewSpecification.elements).filter(elmKey => isViewElementChoise(viewSpecification.elements[elmKey])); + const elementsToRemove = choiseKeys.reduce((acc, cur) => { + const choise = viewSpecification.elements[cur] as ViewElementChoise; + const selectedCase = this.state.choises[cur].selectedCase; + Object.keys(choise.cases).forEach(caseKey => { + if (caseKey === selectedCase) return; + const caseElements = choise.cases[caseKey].elements; + Object.keys(caseElements).forEach(caseElementKey => { + acc.push(caseElements[caseElementKey]); + }); + }); + return acc; + }, [] as ViewElement[]); + + const viewData = this.state.viewData; + const resultingViewData = viewData && Object.keys(viewData).reduce((acc, cur) => { + if (!elementsToRemove.some(elm => elm.label === cur || elm.id === cur)) { + acc[cur] = viewData[cur]; + } + return acc; + }, {} as { [key: string]: any }); + + this.props.onUpdateData(this.props.vPath!, resultingViewData); } this.setState({ editMode: !editMode }); }}> @@ -431,10 +595,10 @@ class ConfigurationApplicationComponent extends React.Component - { this.renderBreadCrumps() } - { displayAsList && viewData instanceof Array - ? this.renderUIElementList(viewSpecification, keyProperty!, viewData) - : this.renderUIElement(viewSpecification, viewData!, keyProperty, editMode, isNew) + {this.renderBreadCrumps()} + {displayAsList && viewData instanceof Array + ? this.renderUIViewList(viewSpecification, keyProperty!, viewData) + : this.renderUIView(viewSpecification, viewData!, keyProperty, editMode, isNew) }
); diff --git a/sdnr/wt/odlux/apps/configurationApp/src/views/networkElementSelector.tsx b/sdnr/wt/odlux/apps/configurationApp/src/views/networkElementSelector.tsx index 6fd5c8cf0..8155becbb 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/views/networkElementSelector.tsx +++ b/sdnr/wt/odlux/apps/configurationApp/src/views/networkElementSelector.tsx @@ -1,3 +1,21 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + import * as React from 'react'; import { RouteComponentProps, withRouter } from 'react-router-dom'; @@ -29,7 +47,7 @@ class NetworkElementSelectorComponent extends React.Component { this.props.history.push(`${ this.props.match.path }/${row.nodeId}`) }} columns={[ + { this.props.history.push(`${this.props.match.path}/${row.nodeId}`) }} columns={[ { property: "nodeId", title: "Name", type: ColumnType.text }, { property: "isRequired", title: "Required ?", type: ColumnType.boolean }, { property: "host", title: "Host", type: ColumnType.text }, -- cgit 1.2.3-korg