diff options
Diffstat (limited to 'sdnr/wt/odlux')
25 files changed, 680 insertions, 227 deletions
diff --git a/sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts b/sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts index 49c2b9be7..5322e1f6f 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts @@ -115,17 +115,26 @@ class YangLexer { private _processIdentifier(): Token { let endpos = this.pos + 1; - while (endpos < this.buf.length && - this._isAlphanum(this.buf.charAt(endpos))) { - endpos++; + while (endpos < this.buf.length && this._isAlphanum(this.buf.charAt(endpos))) { + ++endpos; + } + + let name = 'IDENTIFIER' + if (this.buf.charAt(endpos) === ":") { + name = 'IDENTIFIERREF'; + ++endpos; + while (endpos < this.buf.length && this._isAlphanum(this.buf.charAt(endpos))) { + ++endpos; + } } const tok = { - name: 'IDENTIFIER', + name: name, value: this.buf.substring(this.pos, endpos), start: this.pos, end: endpos }; + this.pos = endpos; return tok; } @@ -214,7 +223,7 @@ class YangLexer { current = stack.shift() || null; } this.pos++; - } else if (this._isAlpha(char)) { + } else if (this._isAlpha(char) || char === "_") { const key = this._processIdentifier().value; this._skipNontokens(); let peekChar = this.buf.charAt(this.pos); @@ -257,7 +266,7 @@ export class YangParser { private _groupingsToResolve: (() => void)[] = []; private _identityToResolve: (() => void)[] = []; private _unionsToResolve: (() => void)[] = []; - + private _modulesToResolve: (() => void)[] = []; private _modules: { [name: string]: Module } = {}; private _views: ViewSpecification[] = [{ @@ -398,14 +407,16 @@ export class YangParser { // create the root elements for this module module.elements = currentView.elements; - Object.keys(module.elements).forEach(key => { - const viewElement = module.elements[key]; - if (!isViewElementObjectOrList(viewElement)) { - throw new Error(`Module: [${module}]. Only List or Object allowed on root level.`); - } - const viewIdIndex = Number(viewElement.viewId); - module.views[key] = this._views[viewIdIndex]; - this._views[0].elements[key] = module.elements[key]; + this._modulesToResolve.push(() => { + Object.keys(module.elements).forEach(key => { + const viewElement = module.elements[key]; + if (!isViewElementObjectOrList(viewElement)) { + throw new Error(`Module: [${module}]. Only List or Object allowed on root level.`); + } + const viewIdIndex = Number(viewElement.viewId); + module.views[key] = this._views[viewIdIndex]; + this._views[0].elements[key] = module.elements[key]; + }); }); return module; } @@ -482,6 +493,12 @@ export class YangParser { console.warn(error.message); } }); + + this._modulesToResolve.forEach(cb => { + try { cb(); } catch (error) { + console.warn(error.message); + } + }); }; private _nextId = 1; @@ -580,8 +597,16 @@ export class YangParser { } private extractSubViews(statement: Statement, parentId: number, module: Module, currentPath: string): [ViewSpecification, ViewSpecification[]] { - const subViews: ViewSpecification[] = []; + // used for scoped definitions + const context: Module = { + ...module, + typedefs: { + ...module.typedefs + } + }; + const currentId = this.nextId; + const subViews: ViewSpecification[] = []; let elements: ViewElement[] = []; const configValue = this.extractValue(statement, "config"); @@ -590,18 +615,26 @@ export class YangParser { // extract conditions const ifFeature = this.extractValue(statement, "if-feature"); const whenCondition = this.extractValue(statement, "when"); - if (whenCondition) console.warn("Found in [" + module.name + "]" + currentPath + " when: " + whenCondition); + 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) + ); // extract all 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: [${module.name}]${currentPath}. Found container without name.`); + throw new Error(`Module: [${context.name}]${currentPath}. Found container without name.`); } - const [currentView, subViews] = this.extractSubViews(cur, currentId, module, `${currentPath}/${module.name}:${cur.arg}`); + const [currentView, subViews] = this.extractSubViews(cur, currentId, context, `${currentPath}/${context.name}:${cur.arg}`); elements.push({ - id: parentId === 0 ? `${module.name}:${cur.arg}` : cur.arg, + id: parentId === 0 ? `${context.name}:${cur.arg}` : cur.arg, label: cur.arg, uiType: "object", viewId: currentView.id, @@ -618,15 +651,15 @@ export class YangParser { if (lists && lists.length > 0) { subViews.push(...lists.reduce<ViewSpecification[]>((acc, cur) => { if (!cur.arg) { - throw new Error(`Module: [${module.name}]${currentPath}. Found list without name.`); + throw new Error(`Module: [${context.name}]${currentPath}. Found list without name.`); } const key = this.extractValue(cur, "key") || undefined; if (config && !key) { - throw new Error(`Module: [${module.name}]${currentPath}. Found configurable list without key.`); + throw new Error(`Module: [${context.name}]${currentPath}. Found configurable list without key.`); } - const [currentView, subViews] = this.extractSubViews(cur, currentId, module, `${currentPath}/${module.name}:${cur.arg}`); + const [currentView, subViews] = this.extractSubViews(cur, currentId, context, `${currentPath}/${context.name}:${cur.arg}`); elements.push({ - id: parentId === 0 ? `${module.name}:${cur.arg}` : cur.arg, + id: parentId === 0 ? `${context.name}:${cur.arg}` : cur.arg, label: cur.arg, isList: true, uiType: "object", @@ -644,7 +677,7 @@ export class YangParser { const leafLists = this.extractNodes(statement, "leaf-list"); if (leafLists && leafLists.length > 0) { elements.push(...leafLists.reduce<ViewElement[]>((acc, cur) => { - const element = this.getViewElement(cur, module, parentId, currentPath, true); + const element = this.getViewElement(cur, context, parentId, currentPath, true); element && acc.push(element); return acc; }, [])); @@ -655,7 +688,7 @@ export class YangParser { const leafs = this.extractNodes(statement, "leaf"); if (leafs && leafs.length > 0) { elements.push(...leafs.reduce<ViewElement[]>((acc, cur) => { - const element = this.getViewElement(cur, module, parentId, currentPath, false); + const element = this.getViewElement(cur, context, parentId, currentPath, false); element && acc.push(element); return acc; }, [])); @@ -666,7 +699,7 @@ export class YangParser { if (choiceStms && choiceStms.length > 0) { elements.push(...choiceStms.reduce<ViewElementChoise[]>((accChoise, curChoise) => { if (!curChoise.arg) { - throw new Error(`Module: [${module.name}]${currentPath}. Found choise without name.`); + 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 } }[] = []; @@ -674,14 +707,14 @@ export class YangParser { if (caseStms && caseStms.length > 0) { cases.push(...caseStms.reduce((accCase, curCase) => { if (!curCase.arg) { - throw new Error(`Module: [${module.name}]${currentPath}/${curChoise.arg}. Found case without name.`); + throw new Error(`Module: [${context.name}]${currentPath}/${curChoise.arg}. Found case without name.`); } const description = this.extractValue(curCase, "description") || undefined; - const [caseView, caseSubViews] = this.extractSubViews(curCase, parentId, module, `${currentPath}/${module.name}:${curChoise.arg}`); + const [caseView, caseSubViews] = this.extractSubViews(curCase, parentId, context, `${currentPath}/${context.name}:${curChoise.arg}`); subViews.push(caseView, ...caseSubViews); const caseDef: { id: string, label: string, description?: string, elements: { [name: string]: ViewElement } } = { - id: parentId === 0 ? `${module.name}:${curCase.arg}` : curCase.arg, + id: parentId === 0 ? `${context.name}:${curCase.arg}` : curCase.arg, label: curCase.arg, description: description, elements: caseView.elements @@ -692,7 +725,7 @@ export class YangParser { } // extract all simple cases (one case per leaf, container, etc.) - const [choiseView, choiseSubViews] = this.extractSubViews(curChoise, parentId, module, `${currentPath}/${module.name}:${curChoise.arg}`); + 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]; @@ -714,7 +747,7 @@ export class YangParser { const element: ViewElementChoise = { uiType: "choise", - id: parentId === 0 ? `${module.name}:${curChoise.arg}` : curChoise.arg, + id: parentId === 0 ? `${context.name}:${curChoise.arg}` : curChoise.arg, label: curChoise.arg, config: config, mandatory: mandatory, @@ -736,7 +769,7 @@ export class YangParser { } if (!statement.arg) { - throw new Error(`Module: [${module.name}]. Found statement without name.`); + throw new Error(`Module: [${context.name}]. Found statement without name.`); } const viewSpec: ViewSpecification = { @@ -772,13 +805,13 @@ export class YangParser { for (let i = 0; i < usesRefs.length; ++i) { const groupingName = usesRefs[i].arg; if (!groupingName) { - throw new Error(`Module: [${module.name}]. Found an uses statement without a grouping name.`); + throw new Error(`Module: [${context.name}]. Found an uses statement without a grouping name.`); } - viewSpec.uses.push(this.resolveReferencePath(groupingName, module)); + viewSpec.uses.push(this.resolveReferencePath(groupingName, context)); this._groupingsToResolve.push(() => { - const groupingViewSpec = this.resolveGrouping(groupingName, module); + const groupingViewSpec = this.resolveGrouping(groupingName, context); if (groupingViewSpec) { Object.keys(groupingViewSpec.elements).forEach(key => { const elm = groupingViewSpec.elements[key]; @@ -1140,6 +1173,13 @@ export class YangParser { uiType: "binary", length: extractRange(0, +18446744073709551615, "length"), }; + } else if (type === "instance-identifier") { + // https://tools.ietf.org/html/rfc7950#page-168 + return { + ...element, + uiType: "string", + length: extractRange(0, +18446744073709551615, "length"), + }; } else { // not a build in type, have to resolve type let typeRef = this.resolveType(type, module); diff --git a/sdnr/wt/odlux/apps/connectApp/src/components/networkElements.tsx b/sdnr/wt/odlux/apps/connectApp/src/components/networkElements.tsx index 0370df7cb..45003e741 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/components/networkElements.tsx +++ b/sdnr/wt/odlux/apps/connectApp/src/components/networkElements.tsx @@ -24,6 +24,7 @@ import LinkOffIcon from '@material-ui/icons/LinkOff'; import RemoveIcon from '@material-ui/icons/RemoveCircleOutline'; import EditIcon from '@material-ui/icons/Edit'; import Info from '@material-ui/icons/Info'; +import ComputerIcon from '@material-ui/icons/Computer'; import { MaterialTable, ColumnType, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; @@ -98,11 +99,42 @@ export class NetworkElementsListComponent extends React.Component<NetworkElement }; } + getContextMenu(rowData: NetworkElementConnection): JSX.Element[] { + + + + const { configuration, fault, inventory } = this.props.applicationState as any; + let buttonArray = [ + <MenuItem aria-label={"mount-button"} onClick={event => this.onOpenMountdNetworkElementsDialog(event, rowData)} ><LinkIcon /><Typography>Mount</Typography></MenuItem>, + <MenuItem aria-label={"unmount-button"} onClick={event => this.onOpenUnmountdNetworkElementsDialog(event, rowData)}><LinkOffIcon /><Typography>Unmount</Typography></MenuItem>, + <Divider />, + <MenuItem aria-label={"info-button"} onClick={event => this.onOpenInfoNetworkElementDialog(event, rowData)} disabled={rowData.status === "Connecting" || rowData.status === "Disconnected"} ><Info /><Typography>Info</Typography></MenuItem>, + <MenuItem aria-label={"edit-button"} onClick={event => this.onOpenEditNetworkElementDialog(event, rowData)}><EditIcon /><Typography>Edit</Typography></MenuItem>, + !rowData.isRequired + ? <MenuItem aria-label={"add-button"} onClick={event => this.onOpenAddNetworkElementDialog(event, rowData)} ><AddIcon /><Typography>Add</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>, + <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>, + ]; + + 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) + } else { + return buttonArray; + } + } + // private navigationCreator render(): JSX.Element { const { classes } = this.props; - const { framework, connect, configuration, fault, help, inventory, maintenance, mediator } = this.props.applicationState as any; const { networkElementToEdit } = this.state; const addRequireNetworkElementAction = { icon: AddIcon, tooltip: 'Add', onClick: () => { @@ -112,7 +144,7 @@ export class NetworkElementsListComponent extends React.Component<NetworkElement }); } }; - let counter = 0; + return ( <> <NetworkElementTable stickyHeader tableId="network-element-table" customActionButtons={[addRequireNetworkElementAction]} columns={[ @@ -124,24 +156,8 @@ export class NetworkElementsListComponent extends React.Component<NetworkElement { property: "coreModelCapability", title: "Core Model", type: ColumnType.text }, { property: "deviceType", title: "Type", type: ColumnType.text }, ]} idProperty="id" {...this.props.networkElementsActions} {...this.props.networkElementsProperties} asynchronus createContextMenu={rowData => { - return [ - <MenuItem onClick={event => this.onOpenMountdNetworkElementsDialog(event, rowData)} ><LinkIcon /><Typography>Mount</Typography></MenuItem>, - <MenuItem onClick={event => this.onOpenUnmountdNetworkElementsDialog(event, rowData)}><LinkOffIcon /><Typography>Unmount</Typography></MenuItem>, - <Divider />, - <MenuItem onClick={event => this.onOpenInfoNetworkElementDialog(event, rowData)} disabled={rowData.status === "Connecting" || rowData.status === "Disconnected"} ><Info /><Typography>Info</Typography></MenuItem>, - <MenuItem onClick={event => this.onOpenEditNetworkElementDialog(event, rowData)}><EditIcon /><Typography>Edit</Typography></MenuItem>, - !rowData.isRequired - ? <MenuItem onClick={event => this.onOpenAddNetworkElementDialog(event, rowData)} ><AddIcon /><Typography>Add</Typography></MenuItem> - : <MenuItem onClick={event => this.onOpenRemoveNetworkElementDialog(event, rowData)} ><RemoveIcon /><Typography>Remove</Typography></MenuItem>, - <Divider />, - <MenuItem onClick={event => this.navigateToApplicationHandlerCreator("inventory", rowData)} disabled={rowData.status === "Connecting" || rowData.status === "Disconnected" || !inventory}><Typography>Inventory</Typography></MenuItem>, - <Divider />, - <MenuItem onClick={event => this.navigateToApplicationHandlerCreator("fault", rowData)} disabled={rowData.status === "Connecting" || rowData.status === "Disconnected" || !fault}><Typography>Fault</Typography></MenuItem>, - <MenuItem onClick={event => this.navigateToApplicationHandlerCreator("configuration", rowData)} disabled={rowData.status === "Connecting" || rowData.status === "Disconnected" || !configuration}><Typography>Configure</Typography></MenuItem>, - <MenuItem onClick={event => this.navigateToApplicationHandlerCreator("accounting", rowData)} disabled={true}><Typography>Accounting</Typography></MenuItem>, - <MenuItem onClick={event => this.navigateToApplicationHandlerCreator("performanceHistory", rowData)} disabled={true}><Typography>Performance</Typography></MenuItem>, - <MenuItem onClick={event => this.navigateToApplicationHandlerCreator("security", rowData)} disabled={true} ><Typography>Security</Typography></MenuItem>, - ]; + + return this.getContextMenu(rowData); }} > </NetworkElementTable> <EditNetworkElementDialog @@ -224,10 +240,6 @@ export class NetworkElementsListComponent extends React.Component<NetworkElement networkElementToEdit: emptyRequireNetworkElement, }); } - - private navigateToApplicationHandlerCreator = (applicationName: string, element: NetworkElementConnection) => (event: React.MouseEvent<HTMLElement>) => { - this.props.navigateToApplication(applicationName, element.nodeId); - } } export const NetworkElementsList = withStyles(styles)(connect(mapProps, mapDispatch)(NetworkElementsListComponent)); diff --git a/sdnr/wt/odlux/apps/faultApp/src/views/faultApplication.tsx b/sdnr/wt/odlux/apps/faultApp/src/views/faultApplication.tsx index 5adc9d49c..cbcfd84d6 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/views/faultApplication.tsx +++ b/sdnr/wt/odlux/apps/faultApp/src/views/faultApplication.tsx @@ -87,8 +87,7 @@ class FaultApplicationComponent extends React.Component<FaultApplicationComponen } private onHandleTabChange = (event: React.ChangeEvent<{}>, newValue: PanelId) => { - this.props.switchActivePanel(newValue); - //this.onToggleTabs(newValue); + this.onToggleTabs(newValue); } private onToggleTabs = (panelId: PanelId) => { diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src/components/editMaintenenceEntryDialog.tsx b/sdnr/wt/odlux/apps/maintenanceApp/src/components/editMaintenenceEntryDialog.tsx index e0cd51493..5ffd98799 100644 --- a/sdnr/wt/odlux/apps/maintenanceApp/src/components/editMaintenenceEntryDialog.tsx +++ b/sdnr/wt/odlux/apps/maintenanceApp/src/components/editMaintenenceEntryDialog.tsx @@ -33,7 +33,7 @@ import { } from '../actions/maintenenceActions'; import { MaintenenceEntry } from '../models/maintenenceEntryType'; -import { FormControl, InputLabel, Select, MenuItem } from '@material-ui/core'; +import { FormControl, InputLabel, Select, MenuItem, Typography } from '@material-ui/core'; export enum EditMaintenenceEntryDialogMode { None = "none", @@ -101,14 +101,15 @@ type EditMaintenenceEntryDIalogComponentProps = Connect<undefined, typeof mapDis onClose: () => void; }; -type EditMaintenenceEntryDIalogComponentState = MaintenenceEntry; +type EditMaintenenceEntryDIalogComponentState = MaintenenceEntry & { isErrorVisible: boolean }; class EditMaintenenceEntryDIalogComponent extends React.Component<EditMaintenenceEntryDIalogComponentProps, EditMaintenenceEntryDIalogComponentState> { - constructor (props: EditMaintenenceEntryDIalogComponentProps) { + constructor(props: EditMaintenenceEntryDIalogComponentProps) { super(props); this.state = { - ...this.props.initialMaintenenceEntry + ...this.props.initialMaintenenceEntry, + isErrorVisible: false }; } @@ -122,11 +123,12 @@ class EditMaintenenceEntryDIalogComponent extends React.Component<EditMaintenenc {setting.dialogDescription} </DialogContentText> <TextField 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 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 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 fullWidth disabled={!setting.enableTimeEditor}> <InputLabel htmlFor="active">Active</InputLabel> - <Select value={ this.state.active || false } onChange={(event) => { + <Select value={this.state.active || false} onChange={(event) => { this.setState({ active: event.target.value as any as boolean }); }} inputProps={{ name: 'active', id: 'active' }} fullWidth > <MenuItem value={true as any as string}>active</MenuItem> @@ -136,14 +138,21 @@ class EditMaintenenceEntryDIalogComponent extends React.Component<EditMaintenenc </DialogContent> <DialogActions> <Button onClick={(event) => { - this.onApply({ - _id: this.state._id || this.state.nodeId, - nodeId: this.state.nodeId, - description: this.state.description, - start: this.state.start, - end: this.state.end, - active: this.state.active - }); + + if (this.props.mode === EditMaintenenceEntryDialogMode.AddMaintenenceEntry && this.state.nodeId.trim().length === 0) { + this.setState({ isErrorVisible: true }); + } else { + this.onApply({ + _id: this.state._id || this.state.nodeId, + nodeId: this.state.nodeId, + description: this.state.description, + start: this.state.start, + end: this.state.end, + active: this.state.active + }); + this.setState({ isErrorVisible: false }); + } + event.preventDefault(); event.stopPropagation(); }} > {setting.applyButtonText} </Button> @@ -151,6 +160,7 @@ class EditMaintenenceEntryDIalogComponent extends React.Component<EditMaintenenc this.onCancel(); event.preventDefault(); event.stopPropagation(); + this.setState({ isErrorVisible: false }); }} color="secondary"> {setting.cancelButtonText} </Button> </DialogActions> </Dialog> diff --git a/sdnr/wt/odlux/apps/mediatorApp/src/components/editMediatorServerDialog.tsx b/sdnr/wt/odlux/apps/mediatorApp/src/components/editMediatorServerDialog.tsx index 882a6b37e..d520db466 100644 --- a/sdnr/wt/odlux/apps/mediatorApp/src/components/editMediatorServerDialog.tsx +++ b/sdnr/wt/odlux/apps/mediatorApp/src/components/editMediatorServerDialog.tsx @@ -161,7 +161,7 @@ class EditMediatorServerDialogComponent extends React.Component<EditMediatorServ <Button onClick={(event) => { if (this.areFieldsValid()) { - this.setState({ errorMessage: [] }) + this.setState({ errorMessage: [] }); this.onApply({ id: this.state.id, name: this.state.name, @@ -177,6 +177,7 @@ class EditMediatorServerDialogComponent extends React.Component<EditMediatorServ }} > {setting.applyButtonText} </Button> <Button onClick={(event) => { this.onCancel(); + this.setState({ errorMessage: [] }); event.preventDefault(); event.stopPropagation(); }} color="secondary"> {setting.cancelButtonText} </Button> diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/ltpAction.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/ltpAction.ts index 2741d889a..375617593 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/ltpAction.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/ltpAction.ts @@ -44,6 +44,12 @@ export class AllAvailableLtpsLoadedAction extends BaseAction { } } +export class SetInitialLoadedAction extends BaseAction { + constructor(public initialLoaded: boolean) { + super(); + } +} + /** * Represents an asynchronous thunk action to load available distinctLtps by networkElement from the database and set the returned first Ltp as default. diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/toggleActions.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/toggleActions.ts new file mode 100644 index 000000000..1f53a5806 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/toggleActions.ts @@ -0,0 +1,15 @@ +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(); + } +} + +export class ResetAllSubViewsAction extends Action { + constructor() { + super(); + } +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/adaptiveModulation.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/adaptiveModulation.tsx index 09d3accd4..c62698630 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/adaptiveModulation.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/adaptiveModulation.tsx @@ -28,13 +28,18 @@ 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 } from '../actions/toggleActions'; const mapProps = (state: IApplicationStoreState) => ({ adaptiveModulationProperties: createAdaptiveModulationProperties(state), + currentView: state.performanceHistory.subViews.adaptiveModulation, + }); const mapDisp = (dispatcher: IDispatcher) => ({ adaptiveModulationActions: createAdaptiveModulationActions(dispatcher.dispatch), + setSubView: (value: "chart" | "table") => dispatcher.dispatch(new SetSubViewAction("adaptiveModulation", value)), }); type AdaptiveModulationComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp> & { @@ -47,6 +52,10 @@ 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>{ + onChange = (value: "chart" | "table") => { + this.props.setSubView(value); + } + render(): JSX.Element { const properties = this.props.adaptiveModulationProperties; const actions = this.props.adaptiveModulationActions; @@ -69,8 +78,10 @@ class AdaptiveModulationComponent extends React.Component<AdaptiveModulationComp return ( <> - {lineChart(chartPagedData)} - <AdaptiveModulationTable idProperty={"_id"} columns={adaptiveModulationColumns} {...properties} {...actions} /> + <ToggleContainer selectedValue={this.props.currentView} onChange={this.onChange}> + {lineChart(chartPagedData)} + <AdaptiveModulationTable idProperty={"_id"} columns={adaptiveModulationColumns} {...properties} {...actions} /> + </ToggleContainer> </> ); }; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/crossPolarDiscrimination.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/crossPolarDiscrimination.tsx index 267e00bbe..7489757f5 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/crossPolarDiscrimination.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/crossPolarDiscrimination.tsx @@ -28,13 +28,19 @@ import { IDataSet, IDataSetsObject } from '../models/chartTypes'; import { createCrossPolarDiscriminationProperties, createCrossPolarDiscriminationActions } from '../handlers/crossPolarDiscriminationHandler'; import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils'; import { addColumnLabels } from '../utils/tableUtils'; +import { SetSubViewAction } from '../actions/toggleActions'; +import ToggleContainer from './toggleContainer'; + const mapProps = (state: IApplicationStoreState) => ({ crossPolarDiscriminationProperties: createCrossPolarDiscriminationProperties(state), + currentView: state.performanceHistory.subViews.CPD, + }); const mapDisp = (dispatcher: IDispatcher) => ({ crossPolarDiscriminationActions: createCrossPolarDiscriminationActions(dispatcher.dispatch), + setSubView: (value: "chart" | "table") => dispatcher.dispatch(new SetSubViewAction("CPD", value)), }); type CrossPolarDiscriminationComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp> & { @@ -47,6 +53,11 @@ 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>{ + + onChange = (value: "chart" | "table") => { + this.props.setSubView(value); + } + render(): JSX.Element { const properties = this.props.crossPolarDiscriminationProperties; const actions = this.props.crossPolarDiscriminationActions; @@ -70,8 +81,10 @@ class CrossPolarDiscriminationComponent extends React.Component<CrossPolarDiscri }); return ( <> - {lineChart(chartPagedData)} - <CrossPolarDiscriminationTable idProperty={"_id"} columns={cpdColumns} {...properties} {...actions} /> + <ToggleContainer selectedValue={this.props.currentView} onChange={this.onChange}> + {lineChart(chartPagedData)} + <CrossPolarDiscriminationTable idProperty={"_id"} columns={cpdColumns} {...properties} {...actions} /> + </ToggleContainer> </> ); }; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/ltpSelection.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/ltpSelection.tsx new file mode 100644 index 000000000..8327ec4ed --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/ltpSelection.tsx @@ -0,0 +1,87 @@ +/** + * ============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 { MenuItem, Select, FormControl } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import { LtpIds } from 'models/availableLtps'; +import { Loader } from '../../../../framework/src/components/material-ui'; + + +const useStyles = makeStyles(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 26px 5px 12px", + transition: theme.transitions.create(["border-color", "box-shadow"]), + }, + center: { + "flex": "1", + "height": "100%", + "display": "flex", + "alignItems": "center", + "justifyContent": "center", + } +})); + +type LtpSelectionProps = { selectedNE: string, finishedLoading: boolean, selectedLtp: string, availableLtps: LtpIds[], onChangeLtp(event: React.ChangeEvent<HTMLSelectElement>): void, selectedTimePeriod: string, onChangeTimePeriod(event: React.ChangeEvent<HTMLSelectElement>): void }; + +export const LtpSelection = (props: LtpSelectionProps) => { + const classes = useStyles(); + return ( + <> + <h3>Selected Network Element: {props.selectedNE} </h3> + <FormControl className={classes.display}> + <span> + Select LTP + </span> + <Select className={classes.selectDropdown} value={props.selectedLtp} onChange={props.onChangeLtp} > + <MenuItem value={"-1"}><em>--Select--</em></MenuItem> + {props.availableLtps.map(ltp => + (<MenuItem value={ltp.key} key={ltp.key}>{ltp.key}</MenuItem>))} + </Select> + <span> Time-Period </span> + <Select className={classes.selectDropdown} value={props.selectedTimePeriod} onChange={props.onChangeTimePeriod} > + <MenuItem value={"15min"}>15min</MenuItem> + <MenuItem value={"24hours"}>24hours</MenuItem> + </Select> + </FormControl> + { + !props.finishedLoading && + <div className={classes.center}> + <Loader /> + <h3>Collecting Data ...</h3> + </div> + } + { + props.selectedLtp === "-1" && props.finishedLoading && + <div className={classes.center}> + <h3>Please select a LTP</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 c58c49c9e..d0caa6fd6 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/performanceData.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/performanceData.tsx @@ -27,13 +27,17 @@ import { IDataSet, IDataSetsObject } from '../models/chartTypes'; import { createPerformanceDataProperties, createPerformanceDataActions } from '../handlers/performanceDataHandler'; import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils'; import { addColumnLabels } from '../utils/tableUtils'; +import ToggleContainer from './toggleContainer'; +import { SetSubViewAction } from '../actions/toggleActions'; const mapProps = (state: IApplicationStoreState) => ({ performanceDataProperties: createPerformanceDataProperties(state), + currentView: state.performanceHistory.subViews.performanceDataSelection, }); const mapDisp = (dispatcher: IDispatcher) => ({ performanceDataActions: createPerformanceDataActions(dispatcher.dispatch), + setSubView: (value: "chart" | "table") => dispatcher.dispatch(new SetSubViewAction("performanceData", value)), }); type PerformanceDataComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp> & { @@ -46,6 +50,11 @@ 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>{ + + onChange = (value: "chart" | "table") => { + this.props.setSubView(value); + } + render(): JSX.Element { const properties = this.props.performanceDataProperties; const actions = this.props.performanceDataActions; @@ -68,8 +77,10 @@ class PerformanceDataComponent extends React.Component<PerformanceDataComponentP }); return ( <> - {lineChart(chartPagedData)} - <PerformanceDataTable idProperty={"_id"} columns={performanceColumns} {...properties} {...actions} /> + <ToggleContainer selectedValue={this.props.currentView} onChange={this.onChange}> + {lineChart(chartPagedData)} + <PerformanceDataTable idProperty={"_id"} columns={performanceColumns} {...properties} {...actions} /> + </ToggleContainer> </> ); }; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/receiveLevel.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/receiveLevel.tsx index 55bc39860..4b0a835d4 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/receiveLevel.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/receiveLevel.tsx @@ -23,18 +23,24 @@ import { MaterialTable, ColumnType, ColumnModel, MaterialTableCtorType } from '. import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect'; -import { ReceiveLevelDataType,ReceiveLevelDatabaseDataType } from '../models/receiveLevelDataType'; +import { ReceiveLevelDataType, ReceiveLevelDatabaseDataType } from '../models/receiveLevelDataType'; import { IDataSet, IDataSetsObject } from '../models/chartTypes'; import { createReceiveLevelProperties, createReceiveLevelActions } from '../handlers/receiveLevelHandler'; import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils'; import { addColumnLabels } from '../utils/tableUtils'; +import { SetSubViewAction } from '../actions/toggleActions'; +import ToggleContainer from './toggleContainer'; const mapProps = (state: IApplicationStoreState) => ({ receiveLevelProperties: createReceiveLevelProperties(state), + currentView: state.performanceHistory.subViews.receiveLevelDataSelection, + }); const mapDisp = (dispatcher: IDispatcher) => ({ receiveLevelActions: createReceiveLevelActions(dispatcher.dispatch), + setSubView: (value: "chart" | "table") => dispatcher.dispatch(new SetSubViewAction("receiveLevel", value)), + }); type ReceiveLevelComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp> & { @@ -47,6 +53,11 @@ 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>{ + + onChange = (value: "chart" | "table") => { + this.props.setSubView(value); + } + render(): JSX.Element { const properties = this.props.receiveLevelProperties; const actions = this.props.receiveLevelActions; @@ -70,8 +81,10 @@ class ReceiveLevelComponent extends React.Component<ReceiveLevelComponentProps>{ return ( <> - {lineChart(chartPagedData)} - <ReceiveLevelTable idProperty={"_id"} columns={receiveLevelColumns} {...properties} {...actions} /> + <ToggleContainer selectedValue={this.props.currentView} onChange={this.onChange}> + {lineChart(chartPagedData)} + <ReceiveLevelTable idProperty={"_id"} columns={receiveLevelColumns} {...properties} {...actions} /> + </ToggleContainer> </> ); }; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/signalToInterference.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/signalToInterference.tsx index 42aa007a9..ba480d57d 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/signalToInterference.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/signalToInterference.tsx @@ -28,13 +28,17 @@ import { IDataSet, IDataSetsObject } from '../models/chartTypes'; import { createSignalToInterferenceProperties, createSignalToInterferenceActions } from '../handlers/signalToInterferenceHandler'; import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils'; import { addColumnLabels } from '../utils/tableUtils'; +import { SetSubViewAction } from '../actions/toggleActions'; +import ToggleContainer from './toggleContainer'; const mapProps = (state: IApplicationStoreState) => ({ signalToInterferenceProperties: createSignalToInterferenceProperties(state), + currentView: state.performanceHistory.subViews.SINR, }); const mapDisp = (dispatcher: IDispatcher) => ({ signalToInterferenceActions: createSignalToInterferenceActions(dispatcher.dispatch), + setSubView: (value: "chart" | "table") => dispatcher.dispatch(new SetSubViewAction("SINR", value)), }); type SignalToInterferenceComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp> & { @@ -47,6 +51,11 @@ 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>{ + + onChange = (value: "chart" | "table") => { + this.props.setSubView(value); + } + render(): JSX.Element { const properties = this.props.signalToInterferenceProperties; const actions = this.props.signalToInterferenceActions; @@ -70,9 +79,11 @@ class SignalToInterferenceComponent extends React.Component<SignalToInterference }); return ( <> - {lineChart(chartPagedData)} - <SignalToInterferenceTable idProperty={"_id"} columns={sinrColumns} {...properties} {...actions} - /> + <ToggleContainer selectedValue={this.props.currentView} onChange={this.onChange}> + {lineChart(chartPagedData)} + <SignalToInterferenceTable idProperty={"_id"} columns={sinrColumns} {...properties} {...actions} + /> + </ToggleContainer> </> ); }; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/temperature.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/temperature.tsx index 256911c2d..28f75d84c 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/temperature.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/temperature.tsx @@ -28,13 +28,17 @@ import { IDataSet, IDataSetsObject } from '../models/chartTypes'; import { createTemperatureProperties, createTemperatureActions } from '../handlers/temperatureHandler'; import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils'; import { addColumnLabels } from '../utils/tableUtils'; +import ToggleContainer from './toggleContainer'; +import { SetSubViewAction } from '../actions/toggleActions'; const mapProps = (state: IApplicationStoreState) => ({ temperatureProperties: createTemperatureProperties(state), + currentView: state.performanceHistory.subViews.temperatur, }); const mapDisp = (dispatcher: IDispatcher) => ({ temperatureActions: createTemperatureActions(dispatcher.dispatch), + setSubView: (value: "chart" | "table") => dispatcher.dispatch(new SetSubViewAction("Temp", value)), }); type TemperatureComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp> & { @@ -47,6 +51,11 @@ 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>{ + + onChange = (value: "chart" | "table") => { + this.props.setSubView(value); + } + render(): JSX.Element { const properties = this.props.temperatureProperties; const actions = this.props.temperatureActions; @@ -69,8 +78,11 @@ class TemperatureComponent extends React.Component<TemperatureComponentProps>{ }); return ( <> - {lineChart(chartPagedData)} - <TemperatureTable idProperty={"_id"} columns={temperatureColumns} {...properties} {...actions} /> + + <ToggleContainer selectedValue={this.props.currentView} onChange={this.onChange}> + {lineChart(chartPagedData)} + <TemperatureTable idProperty={"_id"} columns={temperatureColumns} {...properties} {...actions} /> + </ToggleContainer> </> ); }; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/toggleContainer.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/toggleContainer.tsx new file mode 100644 index 000000000..97a2006f7 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/toggleContainer.tsx @@ -0,0 +1,68 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + +import * as React from 'react'; +import ToggleButton from '@material-ui/lab/ToggleButton'; +import ToggleButtonGroup from '@material-ui/lab/ToggleButtonGroup'; +import BarChartIcon from '@material-ui/icons/BarChart'; +import TableChartIcon from '@material-ui/icons/TableChart'; +import { makeStyles } from '@material-ui/core'; +import Tooltip from '@material-ui/core/Tooltip'; + +const styles = makeStyles({ + toggleButton: { + alignItems: "center", + justifyContent: "center", + padding: "10px", + } +}); + +type toggleProps = { selectedValue: string, onChange(value: string): void }; + +const ToggleContainer: React.FunctionComponent<toggleProps> = (props) => { + + const classes = styles(); + + const handleChange = (event: React.MouseEvent<HTMLElement>, newView: string) => { + if (newView !== null) { + props.onChange(newView) + } + }; + + const children = React.Children.toArray(props.children); + + return ( + <> + <ToggleButtonGroup className={classes.toggleButton} size="medium" value={props.selectedValue} exclusive onChange={handleChange}> + <ToggleButton aria-label="display-chart" key={1} value="chart"> + <Tooltip title="Chart"> + <BarChartIcon /> + </Tooltip> + </ToggleButton> + <ToggleButton aria-label="display-table" key={2} value="table"> + <Tooltip title="Table"> + <TableChartIcon /> + </Tooltip> + </ToggleButton> + </ToggleButtonGroup> + {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 635cbf103..6fe66d2b5 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/transmissionPower.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/transmissionPower.tsx @@ -28,13 +28,17 @@ import { IDataSet, IDataSetsObject } from '../models/chartTypes'; import { createTransmissionPowerProperties, createTransmissionPowerActions } from '../handlers/transmissionPowerHandler'; import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils'; import { addColumnLabels } from '../utils/tableUtils'; +import { SetSubViewAction } from '../actions/toggleActions'; +import ToggleContainer from './toggleContainer'; const mapProps = (state: IApplicationStoreState) => ({ transmissionPowerProperties: createTransmissionPowerProperties(state), + currentView: state.performanceHistory.subViews.transmissionPower, }); const mapDisp = (dispatcher: IDispatcher) => ({ transmissionPowerActions: createTransmissionPowerActions(dispatcher.dispatch), + setSubView: (value: "chart" | "table") => dispatcher.dispatch(new SetSubViewAction("transmissionPower", value)), }); type TransmissionPowerComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp> & { @@ -47,6 +51,11 @@ const TransmissionPowerTable = MaterialTable as MaterialTableCtorType<Transmissi * The Component which gets the transmission power data from the database based on the selected time period. */ class TransmissionPowerComponent extends React.Component<TransmissionPowerComponentProps>{ + + onChange = (value: "chart" | "table") => { + this.props.setSubView(value); + } + render(): JSX.Element { const properties = this.props.transmissionPowerProperties const actions = this.props.transmissionPowerActions @@ -71,8 +80,10 @@ class TransmissionPowerComponent extends React.Component<TransmissionPowerCompon return ( <> - {lineChart(chartPagedData)} - <TransmissionPowerTable idProperty={"_id"} columns={transmissionColumns} {...properties} {...actions} /> + <ToggleContainer selectedValue={this.props.currentView} onChange={this.onChange}> + {lineChart(chartPagedData)} + <TransmissionPowerTable idProperty={"_id"} columns={transmissionColumns} {...properties} {...actions} /> + </ToggleContainer> </> ); }; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/availableLtpsActionHandler.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/availableLtpsActionHandler.ts index b3b284b42..fd137fe37 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/availableLtpsActionHandler.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/availableLtpsActionHandler.ts @@ -20,6 +20,7 @@ import { IActionHandler } from '../../../../framework/src/flux/action'; import { AllAvailableLtpsLoadedAction, LoadAllAvailableLtpsAction, + SetInitialLoadedAction, } from '../actions/ltpAction'; import { LtpIds } from '../models/availableLtps'; @@ -27,11 +28,13 @@ import { LtpIds } from '../models/availableLtps'; export interface IAvailableLtpsState { distinctLtps: LtpIds[]; busy: boolean; + loadedOnce: boolean; } const ltpListStateInit: IAvailableLtpsState = { distinctLtps: [], - busy: false + busy: false, + loadedOnce: false }; export const availableLtpsActionHandler: IActionHandler<IAvailableLtpsState> = (state = ltpListStateInit, action) => { @@ -47,14 +50,22 @@ export const availableLtpsActionHandler: IActionHandler<IAvailableLtpsState> = ( state = { ...state, distinctLtps: action.availableLtps, - busy: false - }; - } else { - state = { - ...state, - busy: false + busy: false, + loadedOnce: true }; } + } else if (action instanceof SetInitialLoadedAction) { + + state = { + ...state, + loadedOnce: action.initialLoaded + }; + } else { + state = { + ...state, + busy: false + }; } + return state; };
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/performanceHistoryRootHandler.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/performanceHistoryRootHandler.ts index c33f10a7e..e57f3860c 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/performanceHistoryRootHandler.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/performanceHistoryRootHandler.ts @@ -37,6 +37,8 @@ import { IAvailableLtpsState, availableLtpsActionHandler } from './availableLtps import { PmDataInterval } from '../models/performanceDataType'; import { TimeChangeAction } from '../actions/timeChangeAction'; import { UpdateMountId } from '../actions/deviceListActions'; +import { SetSubViewAction, ResetAllSubViewsAction } from '../actions/toggleActions'; +import { SubTabType } from '../models/toggleDataType'; export interface IPerformanceHistoryStoreState { nodeId: string; @@ -51,6 +53,7 @@ export interface IPerformanceHistoryStoreState { crossPolarDiscrimination: ICrossPolarDiscriminationState; currentOpenPanel: string | null; pmDataIntervalType: PmDataInterval; + subViews: toggleViewDataType; } const mountIdHandler: IActionHandler<string> = (state = "", action) => { @@ -78,6 +81,29 @@ const currentPMDataIntervalHandler: IActionHandler<PmDataInterval> = (state = Pm return state; } + +type toggleViewDataType = { performanceDataSelection: SubTabType, receiveLevelDataSelection: SubTabType, transmissionPower: SubTabType, adaptiveModulation: SubTabType, temperatur: SubTabType, SINR: SubTabType, CPD: SubTabType }; + + +const toogleViewDataHandler: IActionHandler<toggleViewDataType> = (state = { performanceDataSelection: "chart", receiveLevelDataSelection: "chart", adaptiveModulation: "chart", transmissionPower: "chart", temperatur: "chart", SINR: "chart", CPD: "chart" }, action) => { + + if (action instanceof SetSubViewAction) { + switch (action.currentView) { + case "performanceData": state = { ...state, performanceDataSelection: action.selectedTab }; break; + case "adaptiveModulation": state = { ...state, adaptiveModulation: action.selectedTab }; break; + case "receiveLevel": state = { ...state, receiveLevelDataSelection: action.selectedTab }; break; + case "transmissionPower": state = { ...state, transmissionPower: action.selectedTab }; break; + case "Temp": state = { ...state, temperatur: action.selectedTab }; break; + case "SINR": state = { ...state, SINR: action.selectedTab }; break; + case "CPD": state = { ...state, CPD: action.selectedTab }; break; + } + } else if (action instanceof ResetAllSubViewsAction) { + state = { performanceDataSelection: "chart", adaptiveModulation: "chart", receiveLevelDataSelection: "chart", transmissionPower: "chart", temperatur: "chart", SINR: "chart", CPD: "chart" } + } + + return state; +} + declare module '../../../../framework/src/store/applicationStore' { interface IApplicationStoreState { performanceHistory: IPerformanceHistoryStoreState; @@ -97,7 +123,8 @@ const actionHandlers = { signalToInterference: signalToInterferenceActionHandler, crossPolarDiscrimination: crossPolarDiscriminationActionHandler, currentOpenPanel: currentOpenPanelHandler, - pmDataIntervalType: currentPMDataIntervalHandler + pmDataIntervalType: currentPMDataIntervalHandler, + subViews: toogleViewDataHandler }; const performanceHistoryRootHandler = combineActionHandler<IPerformanceHistoryStoreState>(actionHandlers); diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/toggleDataType.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/toggleDataType.ts new file mode 100644 index 000000000..f705e10b2 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/toggleDataType.ts @@ -0,0 +1,25 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + +/** + * Specifies possible sub views + */ +export type SubTabType = "chart" | "table"; + +export type currentViewType = "performanceData" | "receiveLevel" | "transmissionPower" | "adaptiveModulation" | "Temp" | "SINR" | "CPD"; + diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/chartUtils.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/chartUtils.tsx index 3240f73f1..f58638e30 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/chartUtils.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/chartUtils.tsx @@ -22,7 +22,7 @@ import * as moment from 'moment'; import { ITimeStamp } from 'models/chartTypes'; const style: React.CSSProperties = { - height: "350px" + height: "80%" } export const lineChart = (chartPagedData: IDataSetsObject) => { return ( diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/views/performanceHistoryApplication.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/views/performanceHistoryApplication.tsx index ae22f8041..4984e80c3 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/views/performanceHistoryApplication.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/views/performanceHistoryApplication.tsx @@ -24,7 +24,7 @@ import Select from '@material-ui/core/Select'; import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect'; import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; -import { Panel } from '../../../../framework/src/components/material-ui'; +import { Panel, Loader } from '../../../../framework/src/components/material-ui'; import { NavigateToApplication } from '../../../../framework/src/actions/navigationActions'; import { Dispatch } from '../../../../framework/src/flux/store'; @@ -39,7 +39,7 @@ import SignalToInterference from '../components/signalToInterference'; import CrossPolarDiscrimination from '../components/crossPolarDiscrimination'; import { loadAllDeviceListAsync } from '../actions/deviceListActions'; import { TimeChangeAction } from '../actions/timeChangeAction'; -import { loadDistinctLtpsbyNetworkElementAsync } from '../actions/ltpAction'; +import { loadDistinctLtpsbyNetworkElementAsync, SetInitialLoadedAction } from '../actions/ltpAction'; import { SetPanelAction } from '../actions/panelChangeActions'; import { createPerformanceDataPreActions, performanceDataReloadAction, createPerformanceDataActions } from '../handlers/performanceDataHandler'; import { createReceiveLevelPreActions, receiveLevelReloadAction, createReceiveLevelActions } from '../handlers/receiveLevelHandler'; @@ -50,6 +50,9 @@ import { createSignalToInterferencePreActions, signalToInterferenceReloadAction, import { createCrossPolarDiscriminationPreActions, crossPolarDiscriminationReloadAction, createCrossPolarDiscriminationActions } from '../handlers/crossPolarDiscriminationHandler'; import { MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { AppBar, Tabs, Tab } from '@material-ui/core'; +import LtpSelection from '../components/ltpSelection'; +import { ResetAllSubViewsAction } from '../actions/toggleActions'; const PerformanceHistoryComponentStyles = (theme: Theme) => createStyles({ root: { @@ -58,19 +61,6 @@ const PerformanceHistoryComponentStyles = (theme: Theme) => createStyles({ }, margin: { margin: theme.spacing(1), - }, - display: { - display: "inline-block" - }, - selectDropdown: { - borderRadius: 1, - position: "relative", - backgroundColor: theme.palette.background.paper, - border: "1px solid #ced4da", - fontSize: 16, - width: "auto", - padding: "5px 26px 5px 12px", - transition: theme.transitions.create(["border-color", "box-shadow"]), } }); @@ -79,6 +69,7 @@ const mapProps = (state: IApplicationStoreState) => ({ activePanel: state.performanceHistory.currentOpenPanel, availableLtps: state.performanceHistory.ltps.distinctLtps, networkElements: state.performanceHistory.networkElements.deviceList, + initialLoaded: state.performanceHistory.ltps.loadedOnce }); const mapDispatcher = (dispatcher: IDispatcher) => ({ @@ -111,7 +102,9 @@ const mapDispatcher = (dispatcher: IDispatcher) => ({ timeIntervalChange: (time: PmDataInterval) => dispatcher.dispatch(new TimeChangeAction(time)), changeNode: (nodeId: string) => dispatcher.dispatch((dispatch: Dispatch) => { dispatch(new NavigateToApplication("performanceHistory", nodeId)); - }) + }), + setInitialLoaded: (isLoaded: boolean) => dispatcher.dispatch((dispatch: Dispatch) => { dispatch(new SetInitialLoadedAction(isLoaded)); }), + resetSubViews: () => dispatcher.dispatch(new ResetAllSubViewsAction()) }); export type NetworkElementType = { @@ -127,7 +120,12 @@ type PerformanceHistoryComponentState = { selectedLtp: string, showNetworkElementsTable: boolean, showLtps: boolean, - showPanels: boolean + showPanels: boolean, + preFilter: + { + "node-name": string, + "uuid-interface": string + } | {} }; /** @@ -145,34 +143,70 @@ class PerformanceHistoryComponent extends React.Component<PerformanceHistoryComp selectedLtp: "-1", showNetworkElementsTable: true, showLtps: false, - showPanels: false + showPanels: false, + preFilter: {} }; } - onTogglePanel = (panelId: PanelId) => { - const nextActivePanel = panelId === this.props.activePanel ? null : panelId; + onChangeTabs = (event: React.ChangeEvent<{}>, newValue: PanelId) => { + const nextActivePanel = newValue; + this.changeTabs(nextActivePanel); + } + + changeTabs = (nextActivePanel: PanelId) => { this.props.setCurrentPanel(nextActivePanel); + const preFilter = this.state.preFilter; switch (nextActivePanel) { case "PerformanceData": - this.props.reloadPerformanceData(); + if (this.props.performanceData.preFilter !== {} && this.props.performanceData.preFilter === preFilter) { + this.props.reloadPerformanceData(); + } else { + this.props.performanceDataPreActions.onPreFilterChanged(preFilter); + } break; case "ReceiveLevel": - this.props.reloadReceiveLevel(); + if (this.props.receiveLevel.preFilter !== {} && this.props.receiveLevel.preFilter === preFilter) { + this.props.reloadReceiveLevel(); + } + else { + this.props.receiveLevelPreActions.onPreFilterChanged(preFilter); + } break; case "TransmissionPower": - this.props.reloadTransmissionPower(); + if (this.props.transmissionPower.preFilter !== {} && this.props.transmissionPower.preFilter === preFilter) { + this.props.reloadTransmissionPower(); + } + else { + this.props.transmissionPowerPreActions.onPreFilterChanged(preFilter); + } break; case "AdaptiveModulation": - this.props.reloadAdaptiveModulation(); + if (this.props.adaptiveModulation.preFilter !== {} && this.props.adaptiveModulation.preFilter === preFilter) { + this.props.reloadAdaptiveModulation(); + } else { + this.props.adaptiveModulationPreActions.onPreFilterChanged(preFilter); + } break; case "Temperature": - this.props.reloadTemperature(); + if (this.props.temperature.preFilter !== {} && this.props.temperature.preFilter === preFilter) { + this.props.reloadTemperature(); + } else { + this.props.temperaturePreActions.onPreFilterChanged(preFilter); + } break; case "SINR": - this.props.reloadSignalToInterference(); + if (this.props.signalToInterference.preFilter !== {} && this.props.signalToInterference.preFilter === preFilter) { + this.props.reloadSignalToInterference(); + } else { + this.props.signalToInterferencePreActions.onPreFilterChanged(preFilter); + } break; case "CPD": - this.props.reloadCrossPolarDiscrimination(); + if (this.props.crossPolarDiscrimination.preFilter !== {} && this.props.crossPolarDiscrimination.preFilter === preFilter) { + this.props.reloadCrossPolarDiscrimination(); + } else { + this.props.crossPolarDiscriminationPreActions.onPreFilterChanged(preFilter); + } break; default: // do nothing if all panels are closed @@ -181,7 +215,6 @@ class PerformanceHistoryComponent extends React.Component<PerformanceHistoryComp } render(): JSX.Element { - const { classes } = this.props; const { activePanel, nodeId } = this.props; if (nodeId === "") { return ( @@ -194,59 +227,70 @@ class PerformanceHistoryComponent extends React.Component<PerformanceHistoryComp ) } else { - 3 this.handleURLChange(nodeId); return ( <> - <h3>Selected Network Element: {this.state.selectedNetworkElement} </h3> - {this.state.showLtps && ( - <div> - <FormControl className={classes.display}> - <span> - Select LTP - </span> - <Select className={classes.selectDropdown} value={this.state.selectedLtp} onChange={this.handleLtpChange} > - <MenuItem value={"-1"}><em>--Select--</em></MenuItem> - {this.props.availableLtps.map(ltp => - (<MenuItem value={ltp.key} key={ltp.key}>{ltp.key}</MenuItem>))} - </Select> - <span> Time-Period </span> - <Select className={classes.selectDropdown} value={this.state.selectedTimePeriod} onChange={this.handleTimePeriodChange} > - <MenuItem value={"15min"}>15min</MenuItem> - <MenuItem value={"24hours"}>24hours</MenuItem> - </Select> - </FormControl> - {this.state.showPanels && ( - <div> - <Panel activePanel={activePanel} panelId={"PerformanceData"} onToggle={this.onTogglePanel} title={"Performance Data"}> - <PerformanceData selectedTimePeriod={this.state.selectedTimePeriod} /> - </Panel> - <Panel activePanel={activePanel} panelId={"ReceiveLevel"} onToggle={this.onTogglePanel} title={"Receive Level"}> - <ReceiveLevel selectedTimePeriod={this.state.selectedTimePeriod} /> - </Panel> - <Panel activePanel={activePanel} panelId={"TransmissionPower"} onToggle={this.onTogglePanel} title={"Transmission Power"}> - <TransmissionPower selectedTimePeriod={this.state.selectedTimePeriod} /> - </Panel> - <Panel activePanel={activePanel} panelId={"AdaptiveModulation"} onToggle={this.onTogglePanel} title={"Adaptive Modulation"}> - <AdaptiveModulation selectedTimePeriod={this.state.selectedTimePeriod} /> - </Panel> - <Panel activePanel={activePanel} panelId={"Temperature"} onToggle={this.onTogglePanel} title={"Temperature"}> - <Temperature selectedTimePeriod={this.state.selectedTimePeriod} /> - </Panel> - <Panel activePanel={activePanel} panelId={"SINR"} onToggle={this.onTogglePanel} title={"Signal-to-interference-plus-noise ratio (SINR)"}> - <SignalToInterference selectedTimePeriod={this.state.selectedTimePeriod} /> - </Panel> - <Panel activePanel={activePanel} panelId={"CPD"} onToggle={this.onTogglePanel} title={"Cross Polar Discrimination"}> - <CrossPolarDiscrimination selectedTimePeriod={this.state.selectedTimePeriod} /> - </Panel> - </div> - )} - </div> - )} + {this.state.showLtps && + + <LtpSelection selectedNE={this.state.selectedNetworkElement} selectedLtp={this.state.selectedLtp} selectedTimePeriod={this.state.selectedTimePeriod} + availableLtps={this.props.availableLtps} finishedLoading={this.props.initialLoaded} onChangeTimePeriod={this.handleTimePeriodChange} + onChangeLtp={this.handleLtpChange} + /> + } + {this.state.showPanels && + <> + + <AppBar position="static" > + <Tabs value={activePanel} onChange={this.onChangeTabs} variant="scrollable" scrollButtons="auto" aria-label="performance data tabs"> + <Tab label="Performance Data" value="PerformanceData" /> + <Tab label="Receive Level" value="ReceiveLevel" /> + <Tab label="Transmission Power" value="TransmissionPower" /> + <Tab label="Adaptive Modulation" value="AdaptiveModulation" /> + <Tab label="Temperature" value="Temperature" /> + <Tab label="Signal-to-interference-plus-noise ratio (SINR)" value="SINR" /> + <Tab label="Cross Polar Discrimination" value="CPD" /> + </Tabs> + </AppBar> + { + activePanel === "PerformanceData" && + <PerformanceData selectedTimePeriod={this.state.selectedTimePeriod} /> + } + + { + activePanel === "ReceiveLevel" && + <ReceiveLevel selectedTimePeriod={this.state.selectedTimePeriod} /> + } + + { + activePanel === "TransmissionPower" && + <TransmissionPower selectedTimePeriod={this.state.selectedTimePeriod} /> + } + + { + activePanel === "AdaptiveModulation" && + <AdaptiveModulation selectedTimePeriod={this.state.selectedTimePeriod} /> + } + { + activePanel === "Temperature" && + <Temperature selectedTimePeriod={this.state.selectedTimePeriod} /> + } + + { + activePanel === "SINR" && + <SignalToInterference selectedTimePeriod={this.state.selectedTimePeriod} /> + } + + { + activePanel === "CPD" && + <CrossPolarDiscrimination selectedTimePeriod={this.state.selectedTimePeriod} /> + } + </> + } </> - ); + ) } - }; + } + public componentDidMount() { this.props.getAllDevicesPMdata(); @@ -257,6 +301,8 @@ class PerformanceHistoryComponent extends React.Component<PerformanceHistoryComp this.props.enableFilterAdaptiveModulation.onToggleFilter(); this.props.enableFilterSinr.onToggleFilter(); this.props.enableFilterCpd.onToggleFilter(); + this.props.setInitialLoaded(false); + this.props.resetSubViews(); } /** @@ -268,23 +314,53 @@ class PerformanceHistoryComponent extends React.Component<PerformanceHistoryComp selectedLtp: firstLtp }); this.preFilterChangeAndReload(this.state.selectedNetworkElement, this.state.selectedTimePeriod, firstLtp); + this.changeTabs("PerformanceData"); } /** - * A function which loads the tables based on prefilters defined by network element and ltp on selected time period. + * 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 preFilter = { + const newPreFilter = { "node-name": networkElement, "uuid-interface": ltp }; - this.props.performanceDataPreActions.onPreFilterChanged(preFilter); - this.props.receiveLevelPreActions.onPreFilterChanged(preFilter); - this.props.transmissionPowerPreActions.onPreFilterChanged(preFilter); - this.props.adaptiveModulationPreActions.onPreFilterChanged(preFilter); - this.props.temperaturePreActions.onPreFilterChanged(preFilter); - this.props.signalToInterferencePreActions.onPreFilterChanged(preFilter); - this.props.crossPolarDiscriminationPreActions.onPreFilterChanged(preFilter); + + const activePanel = this.props.activePanel; + + if (this.props.activePanel !== null) { + // set prefilter and reload data if panel is open + + switch (activePanel) { + case "PerformanceData": + this.props.performanceDataPreActions.onPreFilterChanged(newPreFilter); + break; + case "ReceiveLevel": + this.props.receiveLevelPreActions.onPreFilterChanged(newPreFilter); + break; + case "TransmissionPower": + this.props.transmissionPowerPreActions.onPreFilterChanged(newPreFilter); + break; + case "AdaptiveModulation": + this.props.adaptiveModulationPreActions.onPreFilterChanged(newPreFilter); + break; + case "Temperature": + this.props.temperaturePreActions.onPreFilterChanged(newPreFilter); + break; + case "SINR": + this.props.signalToInterferencePreActions.onPreFilterChanged(newPreFilter); + break; + case "CPD": + this.props.crossPolarDiscriminationPreActions.onPreFilterChanged(newPreFilter); + break; + default: + // do nothing if all panels are closed + break; + } + } + + // set prefilter + this.setState({ preFilter: newPreFilter }) } @@ -300,12 +376,19 @@ class PerformanceHistoryComponent extends React.Component<PerformanceHistoryComp showPanels: false, selectedLtp: "-1" }); + + this.props.setInitialLoaded(false); + this.props.resetSubViews(); + this.setState({ preFilter: {} }); this.props.changeNode(selectedNetworkElement); this.props.getDistinctLtpsIds(selectedNetworkElement, this.state.selectedTimePeriod, "-1", this.selectFirstLtp); } private handleURLChange = (selectedNetworkElement: string) => { + if (selectedNetworkElement !== this.state.selectedNetworkElement) { + // gets called if page is reloaded / opened with a networkname in the url, + // not if the selected networkelement is changed this.setState({ showLtps: true, selectedNetworkElement: selectedNetworkElement, @@ -324,6 +407,7 @@ class PerformanceHistoryComponent extends React.Component<PerformanceHistoryComp if (ltpNotSelected) { this.setState({ selectedLtp: "-1", + showPanels: false }); } } @@ -332,21 +416,16 @@ class PerformanceHistoryComponent extends React.Component<PerformanceHistoryComp * Function which handles the time period changes. */ private handleTimePeriodChange = (event: React.ChangeEvent<HTMLSelectElement>) => { + const selectedTimeInterval = event.target.value === "15min" ? PmDataInterval.pmInterval15Min - : PmDataInterval.pmInterval24Hours + : PmDataInterval.pmInterval24Hours; this.setState({ selectedTimePeriod: event.target.value, }); + this.props.timeIntervalChange(selectedTimeInterval); - this.props.reloadPerformanceData(); - this.props.reloadReceiveLevel(); - this.props.reloadTransmissionPower(); - this.props.reloadAdaptiveModulation(); - this.props.reloadTemperature(); - this.props.reloadSignalToInterference(); - this.props.reloadCrossPolarDiscrimination(); this.props.getDistinctLtpsIds(this.state.selectedNetworkElement, event.target.value, this.state.selectedLtp, undefined, this.resetLtpDropdown); this.preFilterChangeAndReload(this.state.selectedNetworkElement, event.target.value, this.state.selectedLtp); } @@ -355,15 +434,22 @@ class PerformanceHistoryComponent extends React.Component<PerformanceHistoryComp * Function which handles the ltp changes. */ private handleLtpChange = (event: React.ChangeEvent<HTMLSelectElement>) => { - var showPanels: boolean = true; + if (event.target.value === "-1") { - showPanels = false; + this.setState({ + showPanels: false, + selectedLtp: event.target.value + }); + this.props.setCurrentPanel(null); + + } else if (event.target.value !== this.state.selectedLtp) { + this.setState({ + showPanels: true, + selectedLtp: event.target.value + }); + this.preFilterChangeAndReload(this.state.selectedNetworkElement, this.state.selectedTimePeriod, event.target.value); + } - this.setState({ - showPanels: showPanels, - selectedLtp: event.target.value - }); - this.preFilterChangeAndReload(this.state.selectedNetworkElement, this.state.selectedTimePeriod, event.target.value); } } diff --git a/sdnr/wt/odlux/framework/pom.xml b/sdnr/wt/odlux/framework/pom.xml index 39856c5c0..15f71f20a 100644 --- a/sdnr/wt/odlux/framework/pom.xml +++ b/sdnr/wt/odlux/framework/pom.xml @@ -46,7 +46,7 @@ <properties> <buildtime>${maven.build.timestamp}</buildtime> <distversion>ONAP Frankfurt (Neon, mdsal ${odl.mdsal.version})</distversion> - <buildno>40.4a1f067(20/02/11)</buildno> + <buildno>41.0f8da02(20/02/20)</buildno> <odlux.version>ONAP SDN-R | ONF Wireless for ${distversion} - Build: ${buildtime} ${buildno} ${project.version}</odlux.version> </properties> diff --git a/sdnr/wt/odlux/framework/src/components/material-table/tableFilter.tsx b/sdnr/wt/odlux/framework/src/components/material-table/tableFilter.tsx index 2075e052c..8ea0a93f4 100644 --- a/sdnr/wt/odlux/framework/src/components/material-table/tableFilter.tsx +++ b/sdnr/wt/odlux/framework/src/components/material-table/tableFilter.tsx @@ -35,6 +35,9 @@ const styles = (theme: Theme) => createStyles({ input: { margin: theme.spacing(1), }, + numberInput: { + float: "right" + } }); interface IEnhancedTableFilterComponentProps extends WithStyles<typeof styles> { @@ -62,6 +65,7 @@ class EnhancedTableFilterComponent extends React.Component<IEnhancedTableFilterC const style = col.width ? { width: col.width } : {}; return ( <TableCell + className={col.type === ColumnType.numeric ? classes.numberInput : ''} key={col.property} padding={col.disablePadding ? 'none' : 'default'} style={style} diff --git a/sdnr/wt/odlux/framework/src/views/login.tsx b/sdnr/wt/odlux/framework/src/views/login.tsx index 3f6ef6134..fa01568ea 100644 --- a/sdnr/wt/odlux/framework/src/views/login.tsx +++ b/sdnr/wt/odlux/framework/src/views/login.tsx @@ -18,13 +18,9 @@ import * as React from 'react'; import { withRouter, RouteComponentProps } from 'react-router-dom'; +import Alert from '@material-ui/lab/Alert'; import Avatar from '@material-ui/core/Avatar'; import Button from '@material-ui/core/Button'; -import Dialog from '@material-ui/core/Dialog'; -import DialogActions from '@material-ui/core/DialogActions'; -import DialogContent from '@material-ui/core/DialogContent'; -import DialogContentText from '@material-ui/core/DialogContentText'; -import DialogTitle from '@material-ui/core/DialogTitle'; import CssBaseline from '@material-ui/core/CssBaseline'; import FormControl from '@material-ui/core/FormControl'; import FormControlLabel from '@material-ui/core/FormControlLabel'; @@ -102,7 +98,7 @@ class LoginComponent extends React.Component<LoginProps, ILoginState> { render(): JSX.Element { const { classes } = this.props; return ( - <React.Fragment> + <> <CssBaseline /> <main className={classes.layout}> <Paper className={classes.paper}> @@ -157,27 +153,10 @@ class LoginComponent extends React.Component<LoginProps, ILoginState> { Sign in </Button> </form> + {this.state.message && <Alert severity="error">{this.state.message}</Alert>} </Paper> </main> - <Dialog - open={!!this.state.message} - onClose={() => { this.setState({ message: '' }) }} - aria-labelledby="alert-dialog-title" - aria-describedby="alert-dialog-description" - > - <DialogTitle id="alert-dialog-title">{"Error"}</DialogTitle> - <DialogContent> - <DialogContentText id="alert-dialog-description"> - {this.state.message} - </DialogContentText> - </DialogContent> - <DialogActions> - <Button onClick={() => { this.setState({ message: '' }) }} color="secondary" autoFocus> - OK - </Button> - </DialogActions> - </Dialog> - </React.Fragment> + </> ); } diff --git a/sdnr/wt/odlux/package.json b/sdnr/wt/odlux/package.json index c6acd563c..b9764f090 100644 --- a/sdnr/wt/odlux/package.json +++ b/sdnr/wt/odlux/package.json @@ -16,6 +16,7 @@ "@fortawesome/react-fontawesome": "0.1.3", "@material-ui/core": "4.9.0", "@material-ui/icons": "4.5.1", + "@material-ui/lab": "4.0.0-alpha.41", "@types/classnames": "2.2.6", "@types/flux": "3.1.8", "@types/jquery": "3.3.10", @@ -81,4 +82,4 @@ "webpack-cli": "3.2.1", "webpack-dev-server": "3.1.14" } -} +}
\ No newline at end of file |