diff options
Diffstat (limited to 'sdnr/wt-odlux/odlux/apps/maintenanceApp')
17 files changed, 1364 insertions, 0 deletions
diff --git a/sdnr/wt-odlux/odlux/apps/maintenanceApp/.babelrc b/sdnr/wt-odlux/odlux/apps/maintenanceApp/.babelrc new file mode 100644 index 000000000..3d8cd1260 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/maintenanceApp/.babelrc @@ -0,0 +1,17 @@ +{ + "presets": [ + ["@babel/preset-react"], + ["@babel/preset-env", { + "targets": { + "chrome": "66" + }, + "spec": true, + "loose": false, + "modules": false, + "debug": false, + "useBuiltIns": "usage", + "forceAllTransforms": true + }] + ], + "plugins": [] +} diff --git a/sdnr/wt-odlux/odlux/apps/maintenanceApp/package.json b/sdnr/wt-odlux/odlux/apps/maintenanceApp/package.json new file mode 100644 index 000000000..d7c325409 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/maintenanceApp/package.json @@ -0,0 +1,46 @@ +{ + "name": "@odlux/maintenance-app", + "version": "0.1.0", + "description": "A react based modular UI for the maintenance app.", + "main": "index.js", + "scripts": { + "start": "webpack-dev-server --env debug", + "build": "webpack --env release --config webpack.config.js", + "build:dev": "webpack --env debug --config webpack.config.js" + }, + "repository": { + "type": "git", + "url": "https://git.mfico.de/highstreet-technologies/odlux.git" + }, + "keywords": [ + "reactjs", + "redux", + "ui", + "framework" + ], + "author": "Matthias Fischer", + "license": "Apache-2.0", + "dependencies": { + "@emotion/react": "^11.7.0", + "@emotion/styled": "^11.6.0", + "@mui/icons-material": "^5.2.0", + "@mui/material": "^5.2.2", + "@mui/styles": "^5.2.2", + "@odlux/connect-app": "*", + "@odlux/framework": "*" + }, + "peerDependencies": { + "@types/classnames": "2.2.6", + "@types/flux": "3.1.8", + "@types/jquery": "3.3.10", + "@types/react": "17.0.37", + "@types/react-dom": "17.0.11", + "@types/react-router-dom": "5.1.7", + "jquery": "3.3.1", + "react": "17.0.2", + "react-dom": "17.0.2", + "react-router-dom": "5.2.0", + "@fortawesome/free-solid-svg-icons": "5.6.3", + "@fortawesome/react-fontawesome": "0.1.14" + } +} diff --git a/sdnr/wt-odlux/odlux/apps/maintenanceApp/pom.xml b/sdnr/wt-odlux/odlux/apps/maintenanceApp/pom.xml new file mode 100644 index 000000000..03832f736 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/maintenanceApp/pom.xml @@ -0,0 +1,109 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ ============LICENSE_START======================================================= + ~ ONAP : SDNR ODLUX + ~ ================================================================================ + ~ Copyright (C) 2020 AT&T 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======================================================= + ~ + --> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>org.onap.ccsdk.features.sdnr.odlux</groupId> + <artifactId>sdnr-odlux-app-maintenanceApp</artifactId> + <version>1.7.0-SNAPSHOT</version> + <packaging>jar</packaging> + + <name>SDNR ODLUX :: ${project.artifactId}</name> + <licenses> + <license> + <name>Apache License, Version 2.0</name> + <url>http://www.apache.org/licenses/LICENSE-2.0</url> + </license> + </licenses> + + <properties> + <maven.javadoc.skip>true</maven.javadoc.skip> + </properties> + + <build> + <resources> + <resource> + <directory>dist</directory> + <targetPath>odlux</targetPath> + </resource> + </resources> + <plugins> + <plugin> + <artifactId>maven-clean-plugin</artifactId> + <configuration> + <filesets> + <fileset> + <directory>dist</directory> + <followSymlinks>false</followSymlinks> + </fileset> + <fileset> + <directory>node</directory> + <followSymlinks>false</followSymlinks> + </fileset> + <fileset> + <directory>node_modules</directory> + <followSymlinks>false</followSymlinks> + </fileset> + <fileset> + <directory>../node_modules</directory> + <followSymlinks>false</followSymlinks> + </fileset> + <!-- eclipse bug build bin folder in basedir --> + <fileset> + <directory>bin</directory> + <followSymlinks>false</followSymlinks> + </fileset> + </filesets> + </configuration> + </plugin> + <plugin> + <groupId>de.jacks-it-lab</groupId> + <artifactId>frontend-maven-plugin</artifactId> + <version>1.7.2</version> + <executions> + <execution> + <id>install node and yarn</id> + <goals> + <goal>install-node-and-yarn</goal> + </goals> + <!-- optional: default phase is "generate-resources" --> + <phase>initialize</phase> + <configuration> + <nodeVersion>v16.17.0</nodeVersion> + <yarnVersion>v1.22.19</yarnVersion> + </configuration> + </execution> + <execution> + <id>yarn build</id> + <goals> + <goal>yarn</goal> + </goals> + <configuration> + <arguments>run build</arguments> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> diff --git a/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/actions/maintenenceActions.ts b/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/actions/maintenenceActions.ts new file mode 100644 index 000000000..740abff85 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/actions/maintenenceActions.ts @@ -0,0 +1,77 @@ +/* eslint-disable @typescript-eslint/no-unused-expressions */ +/** + * ============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 { AddSnackbarNotification } from '../../../../framework/src/actions/snackbarActions'; +import { Action } from '../../../../framework/src/flux/action'; +import { Dispatch } from '../../../../framework/src/flux/store'; + +import { maintenanceEntriesReloadAction } from '../handlers/maintenanceEntriesHandler'; +import { MaintenanceEntry, spoofSymbol } from '../models/maintenanceEntryType'; +import { maintenenceService } from '../services/maintenenceService'; + +export class BaseAction extends Action { } + +export class LoadAllMainteneceEntriesAction extends BaseAction { } + +export class AllMainteneceEntriesLoadedAction extends BaseAction { + + constructor(public maintenenceEntries: MaintenanceEntry[] | null) { + super(); + + } +} + + +export class UpdateMaintenanceEntry extends BaseAction { + constructor(public maintenenceEntry: MaintenanceEntry) { + super(); + } +} + +/** Represents an async thunk action creator to add an element to the maintenence entries. */ +export const addOrUpdateMaintenenceEntryAsyncActionCreator = (entry: MaintenanceEntry) => (dispatch: Dispatch) => { + maintenenceService.writeMaintenenceEntry(entry).then(result => { + result && window.setTimeout(() => { + // dispatch(loadAllMountedNetworkElementsAsync); + dispatch(new UpdateMaintenanceEntry(entry)); + dispatch(new AddSnackbarNotification({ message: `Successfully ${result && result.created ? 'created' : 'updated'} maintenance settings for [${entry.nodeId}]`, options: { variant: 'success' } })); + }, 900); + dispatch(maintenanceEntriesReloadAction); + }); +}; + +/** Represents an async thunk action creator to delete an element from the maintenence entries. */ +export const removeFromMaintenenceEntrysAsyncActionCreator = (entry: MaintenanceEntry) => (dispatch: Dispatch) => { + maintenenceService.deleteMaintenenceEntry(entry).then(result => { + result && window.setTimeout(() => { + dispatch(new UpdateMaintenanceEntry({ + [spoofSymbol]: true, + mId: entry.mId, + nodeId: entry.nodeId, + description: '', + start: '', + end: '', + active: false, + })); + dispatch(new AddSnackbarNotification({ message: `Successfully removed [${entry.nodeId}]`, options: { variant: 'success' } })); + }, 900); + dispatch(maintenanceEntriesReloadAction); + }); +}; + +// Hint: since there is no notification of changed required network elements, this code is not aware of changes caused outiside of this browser.
\ No newline at end of file diff --git a/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/assets/icons/maintenanceAppIcon.svg b/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/assets/icons/maintenanceAppIcon.svg new file mode 100644 index 000000000..8b99a5e7f --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/assets/icons/maintenanceAppIcon.svg @@ -0,0 +1,50 @@ +<!-- highstreet technologies GmbH colour scheme + Grey #565656 + LBlue #36A9E1 + DBlue #246DA2 + Green #003F2C / #006C4B + Yellw #C8D400 + Red #D81036 +--> + +<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 684.000000 684.000000"> + +<g transform="translate(0.000000,684.000000) scale(0.100000,-0.100000)"> + +<path fill="#565656" d="M3044 6693 c-12 -2 -33 -17 -48 -32 -35 -37 -55 -136 -96 -476 -17 +-142 -32 -260 -33 -260 -1 -1 -60 -19 -132 -40 -243 -71 -406 -139 -600 -250 +-99 -57 -120 -66 -138 -57 -12 6 -124 91 -249 189 -124 98 -252 193 -283 212 +-57 34 -57 34 -89 17 -81 -43 -421 -374 -513 -500 -24 -32 -43 -67 -43 -78 0 +-36 98 -173 337 -472 57 -70 103 -132 103 -138 0 -5 -17 -35 -39 -66 -98 -144 +-275 -566 -301 -715 -14 -78 26 -65 -335 -112 -384 -51 -423 -62 -447 -125 -5 +-14 -9 -179 -9 -376 0 -414 0 -416 78 -433 43 -10 213 -35 518 -77 183 -25 +167 -12 221 -182 75 -238 160 -433 278 -637 l35 -61 -175 -219 c-206 -259 +-274 -357 -274 -393 0 -22 50 -76 258 -282 142 -141 275 -269 295 -284 l36 +-28 38 23 c62 38 177 124 380 286 l193 153 92 -57 c168 -102 383 -193 633 +-269 66 -19 123 -39 127 -43 9 -8 19 -76 53 -361 14 -113 33 -243 42 -289 23 +-119 0 -114 455 -113 237 0 373 4 386 11 44 23 61 101 106 485 l32 269 149 46 +c227 71 395 139 395 160 0 5 -125 127 -277 272 -194 186 -283 263 -297 262 +-12 -1 -75 -11 -141 -23 -318 -56 -700 -29 -973 69 -278 101 -476 226 -685 +435 -275 275 -445 609 -504 988 -23 152 -23 432 1 586 60 394 215 705 493 984 +166 169 314 278 498 368 292 143 539 191 912 176 194 -8 297 -24 446 -72 485 +-156 879 -500 1098 -959 134 -278 196 -617 170 -918 -6 -73 -21 -186 -33 -251 +l-22 -119 274 -268 c268 -261 275 -268 292 -248 23 27 88 198 133 351 31 104 +41 127 59 132 11 3 131 19 266 36 402 50 463 65 477 118 5 15 8 192 8 393 0 +353 -1 368 -20 389 -36 40 -79 49 -469 100 -132 18 -249 36 -261 40 -18 7 -29 +33 -59 137 -66 223 -139 392 -292 669 l-28 51 126 159 c193 246 265 343 294 +399 l27 52 -31 45 c-101 147 -505 538 -555 538 -30 0 -234 -146 -506 -362 +l-104 -82 -51 28 c-244 136 -390 201 -606 271 -63 21 -136 46 -161 57 l-46 19 +-36 297 c-50 403 -58 430 -136 452 -34 9 -671 12 -717 3z"/> + +<path fill="#006C4B" d="M3080 4653 c-77 -8 -195 -35 -232 -54 -21 -10 -45 -30 -54 -44 -15 +-23 -15 -27 0 -49 22 -35 111 -101 277 -207 247 -158 343 -239 384 -326 35 +-74 -5 -237 -108 -438 -40 -77 -68 -116 -122 -170 -77 -77 -194 -145 -249 +-145 -39 0 -127 48 -339 184 -194 124 -291 176 -330 176 -60 0 -105 -86 -86 +-167 28 -127 262 -492 423 -662 101 -106 236 -191 333 -211 27 -5 138 -14 248 +-20 372 -20 506 -66 689 -240 87 -83 629 -652 1291 -1355 181 -192 439 -465 +573 -607 244 -255 245 -256 281 -251 220 32 406 139 512 295 50 73 98 174 106 +222 5 32 0 42 -53 108 -113 140 -296 342 -1073 1183 -513 554 -980 1066 -1074 +1178 -117 137 -174 252 -196 394 -6 39 -15 171 -21 294 -6 123 -15 257 -20 +297 -28 207 -209 414 -464 532 -97 45 -172 66 -286 80 -78 9 -330 11 -410 3z"/> +</g> +</svg> diff --git a/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/components/editMaintenenceEntryDialog.tsx b/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/components/editMaintenenceEntryDialog.tsx new file mode 100644 index 000000000..9ab147ca7 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/components/editMaintenenceEntryDialog.tsx @@ -0,0 +1,207 @@ +/* eslint-disable @typescript-eslint/no-unused-expressions */ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ +import * as React from 'react'; + +import Button from '@mui/material/Button'; +import Dialog from '@mui/material/Dialog'; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogContent'; +import DialogContentText from '@mui/material/DialogContentText'; +import DialogTitle from '@mui/material/DialogTitle'; +import TextField from '@mui/material/TextField'; + +import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect'; + +import { FormControl, InputLabel, MenuItem, Select, Typography } from '@mui/material'; +import { + addOrUpdateMaintenenceEntryAsyncActionCreator, + removeFromMaintenenceEntrysAsyncActionCreator, +} from '../actions/maintenenceActions'; +import { MaintenanceEntry } from '../models/maintenanceEntryType'; + +export enum EditMaintenenceEntryDialogMode { + None = 'none', + AddMaintenenceEntry = 'addMaintenenceEntry', + EditMaintenenceEntry = 'editMaintenenceEntry', + RemoveMaintenenceEntry = 'removeMaintenenceEntry', +} + +const mapDispatch = (dispatcher: IDispatcher) => ({ + addOrUpdateMaintenenceEntry: (entry: MaintenanceEntry) => { + dispatcher.dispatch(addOrUpdateMaintenenceEntryAsyncActionCreator(entry)); + }, + removeMaintenenceEntry: (entry: MaintenanceEntry) => { + dispatcher.dispatch(removeFromMaintenenceEntrysAsyncActionCreator(entry)); + }, +}); + +type DialogSettings = { + dialogTitle: string; + dialogDescription: string; + applyButtonText: string; + cancelButtonText: string; + enableMountIdEditor: boolean; + enableTimeEditor: boolean; +}; + +const settings: { [key: string]: DialogSettings } = { + [EditMaintenenceEntryDialogMode.None]: { + dialogTitle: '', + dialogDescription: '', + applyButtonText: '', + cancelButtonText: '', + enableMountIdEditor: false, + enableTimeEditor: false, + }, + [EditMaintenenceEntryDialogMode.AddMaintenenceEntry]: { + dialogTitle: 'Add new maintenence entry', + dialogDescription: '', + applyButtonText: 'Add', + cancelButtonText: 'Cancel', + enableMountIdEditor: true, + enableTimeEditor: true, + }, + [EditMaintenenceEntryDialogMode.EditMaintenenceEntry]: { + dialogTitle: 'Edit maintenence entry', + dialogDescription: '', + applyButtonText: 'Save', + cancelButtonText: 'Cancel', + enableMountIdEditor: false, + enableTimeEditor: true, + }, + [EditMaintenenceEntryDialogMode.RemoveMaintenenceEntry]: { + dialogTitle: 'Remove maintenence entry', + dialogDescription: '', + applyButtonText: 'Remove', + cancelButtonText: 'Cancel', + enableMountIdEditor: false, + enableTimeEditor: false, + }, +}; + +type EditMaintenenceEntryDIalogComponentProps = Connect<undefined, typeof mapDispatch> & { + mode: EditMaintenenceEntryDialogMode; + initialMaintenenceEntry: MaintenanceEntry; + onClose: () => void; +}; + +type EditMaintenenceEntryDIalogComponentState = MaintenanceEntry & { isErrorVisible: boolean }; + +class EditMaintenenceEntryDIalogComponent extends React.Component<EditMaintenenceEntryDIalogComponentProps, EditMaintenenceEntryDIalogComponentState> { + constructor(props: EditMaintenenceEntryDIalogComponentProps) { + super(props); + + this.state = { + ...this.props.initialMaintenenceEntry, + isErrorVisible: false, + }; + } + + render(): JSX.Element { + const setting = settings[this.props.mode]; + return ( + <Dialog open={this.props.mode !== EditMaintenenceEntryDialogMode.None}> + <DialogTitle id="form-dialog-title">{setting.dialogTitle}</DialogTitle> + <DialogContent> + <DialogContentText> + {setting.dialogDescription} + </DialogContentText> + <TextField variant="standard" disabled={!setting.enableMountIdEditor} spellCheck={false} autoFocus margin="dense" id="name" label="Name" type="text" fullWidth value={this.state.nodeId} onChange={(event) => { this.setState({ nodeId: event.target.value }); }} /> + {this.state.isErrorVisible && <Typography variant="body1" color="error" >Name must not be empty.</Typography>} + <TextField variant="standard" disabled={!setting.enableTimeEditor} spellCheck={false} autoFocus margin="dense" id="start" + label="Start (Local DateTime)" type="datetime-local" fullWidth value={this.state.start} onChange={(event) => { this.setState({ start: event.target.value }); }} /> + <TextField variant="standard" disabled={!setting.enableTimeEditor} spellCheck={false} autoFocus margin="dense" id="end" + label="End (Local DateTime)" type="datetime-local" fullWidth value={this.state.end} onChange={(event) => { this.setState({ end: event.target.value }); }} /> + <FormControl variant="standard" fullWidth disabled={!setting.enableTimeEditor}> + <InputLabel htmlFor="active">Active</InputLabel> + <Select variant="standard" 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> + <MenuItem value={false as any as string}>not active</MenuItem> + </Select> + </FormControl> + </DialogContent> + <DialogActions> + <Button onClick={(event) => { + + if (this.props.mode === EditMaintenenceEntryDialogMode.AddMaintenenceEntry && this.state.nodeId.trim().length === 0) { + this.setState({ isErrorVisible: true }); + } else { + this.onApply({ + mId: this.state.mId || this.state.nodeId, + nodeId: this.state.nodeId, + description: this.state.description, + start: this.state.start, + end: this.state.end, + active: this.state.active, + }); + this.setState({ isErrorVisible: false }); + } + + event.preventDefault(); + event.stopPropagation(); + }} color="inherit" > {setting.applyButtonText} </Button> + <Button onClick={(event) => { + this.onCancel(); + event.preventDefault(); + event.stopPropagation(); + this.setState({ isErrorVisible: false }); + }} color="secondary"> {setting.cancelButtonText} </Button> + </DialogActions> + </Dialog> + ); + } + + private onApply = (entry: MaintenanceEntry) => { + this.props.onClose && this.props.onClose(); + switch (this.props.mode) { + case EditMaintenenceEntryDialogMode.AddMaintenenceEntry: + entry && this.props.addOrUpdateMaintenenceEntry(entry); + break; + case EditMaintenenceEntryDialogMode.EditMaintenenceEntry: + entry && this.props.addOrUpdateMaintenenceEntry(entry); + break; + case EditMaintenenceEntryDialogMode.RemoveMaintenenceEntry: + entry && this.props.removeMaintenenceEntry(entry); + break; + } + }; + + + private onCancel = () => { + this.props.onClose && this.props.onClose(); + }; + + static getDerivedStateFromProps(props: EditMaintenenceEntryDIalogComponentProps, state: EditMaintenenceEntryDIalogComponentState & { initialMaintenenceEntrie: MaintenanceEntry }): EditMaintenenceEntryDIalogComponentState & { initialMaintenenceEntrie: MaintenanceEntry } { + if (props.initialMaintenenceEntry !== state.initialMaintenenceEntrie) { + // eslint-disable-next-line no-param-reassign + state = { + ...state, + ...props.initialMaintenenceEntry, + initialMaintenenceEntrie: props.initialMaintenenceEntry, + }; + } + return state; + } + +} + +export const EditMaintenenceEntryDIalog = connect(undefined, mapDispatch)(EditMaintenenceEntryDIalogComponent); +export default EditMaintenenceEntryDIalog;
\ No newline at end of file diff --git a/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/components/refreshMaintenanceEntries.tsx b/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/components/refreshMaintenanceEntries.tsx new file mode 100644 index 000000000..e8bd635dd --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/components/refreshMaintenanceEntries.tsx @@ -0,0 +1,113 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ +import * as React from 'react'; + +import Button from '@mui/material/Button'; +import Dialog from '@mui/material/Dialog'; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogContent'; +import DialogContentText from '@mui/material/DialogContentText'; +import DialogTitle from '@mui/material/DialogTitle'; + +import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect'; + +import { maintenanceEntriesReloadAction } from '../handlers/maintenanceEntriesHandler'; +import { MaintenanceEntry } from '../models/maintenanceEntryType'; + +export enum RefreshMaintenanceEntriesDialogMode { + None = 'none', + RefreshMaintenanceEntriesTable = 'RefreshMaintenanceEntriesTable', +} + +const mapDispatch = (dispatcher: IDispatcher) => ({ + refreshMaintenanceEntries: () => dispatcher.dispatch(maintenanceEntriesReloadAction), +}); + +type DialogSettings = { + dialogTitle: string; + dialogDescription: string; + applyButtonText: string; + cancelButtonText: string; + enableMountIdEditor: boolean; + enableUsernameEditor: boolean; + enableExtendedEditor: boolean; +}; + +const settings: { [key: string]: DialogSettings } = { + [RefreshMaintenanceEntriesDialogMode.None]: { + dialogTitle: '', + dialogDescription: '', + applyButtonText: '', + cancelButtonText: '', + enableMountIdEditor: false, + enableUsernameEditor: false, + enableExtendedEditor: false, + }, + [RefreshMaintenanceEntriesDialogMode.RefreshMaintenanceEntriesTable]: { + dialogTitle: 'Do you want to refresh Maintenance Entries?', + dialogDescription: '', + applyButtonText: 'Yes', + cancelButtonText: 'Cancel', + enableMountIdEditor: true, + enableUsernameEditor: true, + enableExtendedEditor: true, + }, +}; + +type RefreshMaintenanceEntriesDialogComponentProps = Connect<undefined, typeof mapDispatch> & { + mode: RefreshMaintenanceEntriesDialogMode; + onClose: () => void; +}; + +type RefreshMaintenanceEntriesDialogComponentState = MaintenanceEntry & { isNameValid: boolean; isHostSet: boolean }; + +class RefreshMaintenanceEntriesDialogComponent extends React.Component<RefreshMaintenanceEntriesDialogComponentProps, RefreshMaintenanceEntriesDialogComponentState> { + render(): JSX.Element { + const setting = settings[this.props.mode]; + return ( + <Dialog open={this.props.mode !== RefreshMaintenanceEntriesDialogMode.None}> + <DialogTitle id="form-dialog-title" aria-label={`${setting.dialogTitle.replace(/ /g, '-').toLowerCase()}-dialog`}>{setting.dialogTitle}</DialogTitle> + <DialogContent> + <DialogContentText> + {setting.dialogDescription} + </DialogContentText> + </DialogContent> + <DialogActions> + <Button aria-label="dialog-confirm-button" onClick={() => { + this.onRefresh(); + }} color="inherit" > {setting.applyButtonText} </Button> + <Button aria-label="dialog-cancel-button" onClick={() => { + this.onCancel(); + }} color="secondary"> {setting.cancelButtonText} </Button> + </DialogActions> + </Dialog> + ); + } + + private onRefresh = () => { + this.props.refreshMaintenanceEntries(); + this.props.onClose(); + }; + + private onCancel = () => { + this.props.onClose(); + }; +} + +export const RefreshMaintenanceEntriesDialog = connect(undefined, mapDispatch)(RefreshMaintenanceEntriesDialogComponent); +export default RefreshMaintenanceEntriesDialog;
\ No newline at end of file diff --git a/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/handlers/maintenanceAppRootHandler.ts b/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/handlers/maintenanceAppRootHandler.ts new file mode 100644 index 000000000..ced7f2160 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/handlers/maintenanceAppRootHandler.ts @@ -0,0 +1,39 @@ +/** + * ============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========================================================================== + */ +// main state handler + +import { combineActionHandler } from '../../../../framework/src/flux/middleware'; + +import { IMaintenanceEntriesState, maintenanceEntriesActionHandler } from './maintenanceEntriesHandler'; + +export interface IMaintenanceAppStoreState { + maintenanceEntries : IMaintenanceEntriesState; +} + +declare module '../../../../framework/src/store/applicationStore' { + interface IApplicationStoreState { + maintenance: IMaintenanceAppStoreState; + } +} + +const actionHandlers = { + maintenanceEntries: maintenanceEntriesActionHandler, +}; + +export const maintenanceAppRootHandler = combineActionHandler<IMaintenanceAppStoreState>(actionHandlers); +export default maintenanceAppRootHandler; diff --git a/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/handlers/maintenanceEntriesHandler.ts b/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/handlers/maintenanceEntriesHandler.ts new file mode 100644 index 000000000..c3fe51e80 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/handlers/maintenanceEntriesHandler.ts @@ -0,0 +1,35 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ +import { createExternal, IExternalTableState } from '../../../../framework/src/components/material-table/utilities'; +import { createSearchDataHandler } from '../../../../framework/src/utilities/elasticSearch'; + +import { MaintenanceEntry } from '../models/maintenanceEntryType'; +export interface IMaintenanceEntriesState extends IExternalTableState<MaintenanceEntry> { } + +// create elastic search material data fetch handler +const maintenanceEntriesSearchHandler = createSearchDataHandler<MaintenanceEntry>('maintenance'); + +export const { + actionHandler: maintenanceEntriesActionHandler, + createActions: createmaintenanceEntriesActions, + createProperties: createmaintenanceEntriesProperties, + reloadAction: maintenanceEntriesReloadAction, + + // set value action, to change a value +} = createExternal<MaintenanceEntry>(maintenanceEntriesSearchHandler, appState => appState.maintenance.maintenanceEntries); + diff --git a/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/index.html b/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/index.html new file mode 100644 index 000000000..c84aecee1 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/index.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<html lang="en"> + +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <meta http-equiv="X-UA-Compatible" content="ie=edge"> + <!-- <link rel="stylesheet" href="./vendor.css"> --> + <title>Minimal App</title> +</head> + +<body> + <div id="app"></div> + <script type="text/javascript" src="./require.js"></script> + <script type="text/javascript" src="./config.js"></script> + <script> + // run the application + require(["app","connectApp", "maintenanceApp"], function (app, connectApp, maintenanceApp) { + connectApp.register(); + maintenanceApp.register(); + app("./app.tsx").runApplication(); + }); + </script> +</body> + +</html>
\ No newline at end of file diff --git a/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/models/maintenanceEntryType.ts b/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/models/maintenanceEntryType.ts new file mode 100644 index 000000000..27cdc8c12 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/models/maintenanceEntryType.ts @@ -0,0 +1,33 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ +/** Represents the elestic search db type for maintenence enrties */ + + +export const spoofSymbol = Symbol('Spoof'); + +/** Represents the type for an maintenence entry. */ +export type MaintenanceEntry = { + mId: string; + nodeId: string; + description?: string; + end: string; + start: string; + active: boolean; + [spoofSymbol]?: boolean; +}; + diff --git a/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/pluginMaintenance.tsx b/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/pluginMaintenance.tsx new file mode 100644 index 000000000..0f686cb84 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/pluginMaintenance.tsx @@ -0,0 +1,44 @@ +/** + * ============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========================================================================== + */ +// app configuration and main entry point for the app + +import React, { FC } from 'react'; + +import applicationManager from '../../../framework/src/services/applicationManager'; + +import { maintenanceAppRootHandler } from './handlers/maintenanceAppRootHandler'; + +import { MaintenanceView } from './views/maintenanceView'; + +const appIcon = require('./assets/icons/maintenanceAppIcon.svg'); // select app icon + +const App : FC = () => { + return <MaintenanceView />; +}; + +export function register() { + applicationManager.registerApplication({ + name: 'maintenance', + icon: appIcon, + rootComponent: App, + rootActionHandler: maintenanceAppRootHandler, + menuEntry: 'Maintenance', + }); +} + + diff --git a/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/services/maintenenceService.ts b/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/services/maintenenceService.ts new file mode 100644 index 000000000..5fdccc349 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/services/maintenenceService.ts @@ -0,0 +1,72 @@ +/** + * ============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 { DeleteResponse, PostResponse } from '../../../../framework/src/models/elasticSearch'; +import { requestRest } from '../../../../framework/src/services/restService'; +import { convertPropertyNames, replaceUpperCase } from '../../../../framework/src/utilities/yangHelper'; +import { MaintenanceEntry } from '../models/maintenanceEntryType'; + +import { convertToISODateString } from '../utils/timeUtils'; + + +export const maintenenceEntryDatabasePath = 'mwtn/maintenancemode'; + +/** + * Represents a web api accessor service for all maintenence entries related actions. + */ +class MaintenenceService { + + /** + * Adds or updates one maintenence entry to the backend. + */ + public async writeMaintenenceEntry(maintenenceEntry: MaintenanceEntry): Promise<PostResponse | null> { + const path = '/rests/operations/data-provider:create-maintenance'; + + const query = { + 'id': maintenenceEntry.mId, + 'node-id': maintenenceEntry.nodeId, + 'active': maintenenceEntry.active, + 'description': maintenenceEntry.description, + 'end': convertToISODateString(maintenenceEntry.end), + 'start': convertToISODateString(maintenenceEntry.start), + }; + + const result = await requestRest<PostResponse>(path, { method: 'POST', body: JSON.stringify(convertPropertyNames({ 'data-provider:input': query }, replaceUpperCase)) }); + return result || null; + } + + /** + * Deletes one maintenence entry by its mountId from the backend. + */ + public async deleteMaintenenceEntry(maintenenceEntry: MaintenanceEntry): Promise<(DeleteResponse) | null> { + const path = '/rests/operations/data-provider:delete-maintenance'; + + const query = { + 'id': maintenenceEntry.mId, + 'node-id': maintenenceEntry.nodeId, + 'active': maintenenceEntry.active, + 'description': maintenenceEntry.description, + 'end': convertToISODateString(maintenenceEntry.end), + 'start': convertToISODateString(maintenenceEntry.start), + }; + const result = await requestRest<DeleteResponse>(path, { method: 'POST', body: JSON.stringify(convertPropertyNames({ 'data-provider:input': query }, replaceUpperCase)) }); + return result || null; + } +} + +export const maintenenceService = new MaintenenceService(); +export default maintenenceService;
\ No newline at end of file diff --git a/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/utils/timeUtils.ts b/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/utils/timeUtils.ts new file mode 100644 index 000000000..0fde5fcad --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/utils/timeUtils.ts @@ -0,0 +1,45 @@ +/** + * ============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========================================================================== + */ +export function convertToGMTString(dateString: string): string { + const date = new Date(dateString); + const pad = (n: number) => (n < 10) ? '0' + n : n; + + return date.getUTCFullYear() + + '-' + pad(date.getUTCMonth() + 1) + + '-' + pad(date.getUTCDate()) + + 'T' + pad(date.getUTCHours()) + + ':' + pad(date.getUTCMinutes()) + + '+00:00'; +} + +export function convertToLocaleString(rawDate: string | number): string { + const date = new Date(rawDate); + const pad = (n: number) => (n < 10) ? '0' + n : n; + + return date.getFullYear() + + '-' + pad(date.getMonth() + 1) + + '-' + pad(date.getDate()) + + 'T' + pad(date.getHours()) + + ':' + pad(date.getMinutes()); +} + +export function convertToISODateString(rawDate: string | number): string { + const date = new Date(rawDate); + const displayDate = date.toISOString(); + return displayDate.replace(/\.[0-9]{2}/, '.'); +}
\ No newline at end of file diff --git a/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/views/maintenanceView.tsx b/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/views/maintenanceView.tsx new file mode 100644 index 000000000..d54d63c04 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/maintenanceApp/src/views/maintenanceView.tsx @@ -0,0 +1,246 @@ +/** + * ============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 React from 'react'; + +import { faBan } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import AddIcon from '@mui/icons-material/Add'; +import EditIcon from '@mui/icons-material/Edit'; +import Refresh from '@mui/icons-material/Refresh'; +import RemoveIcon from '@mui/icons-material/RemoveCircleOutline'; +import { Divider, MenuItem, Theme, Typography } from '@mui/material'; +import { WithStyles } from '@mui/styles'; +import createStyles from '@mui/styles/createStyles'; +import withStyles from '@mui/styles/withStyles'; + +import MaterialTable, { ColumnType, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; + +import EditMaintenenceEntryDialog, { EditMaintenenceEntryDialogMode } from '../components/editMaintenenceEntryDialog'; +import RefreshMaintenanceEntriesDialog, { RefreshMaintenanceEntriesDialogMode } from '../components/refreshMaintenanceEntries'; +import { createmaintenanceEntriesActions, createmaintenanceEntriesProperties, maintenanceEntriesReloadAction } from '../handlers/maintenanceEntriesHandler'; +import { MaintenanceEntry } from '../models/maintenanceEntryType'; +import { convertToLocaleString } from '../utils/timeUtils'; + +const styles = (theme: Theme) => createStyles({ + button: { + margin: 0, + padding: '6px 6px', + minWidth: 'unset', + }, + spacer: { + marginLeft: theme.spacing(1), + marginRight: theme.spacing(1), + display: 'inline', + }, +}); + +const MaintenanceEntriesTable = MaterialTable as MaterialTableCtorType<MaintenanceEntry>; + +const mapProps = (state: IApplicationStoreState) => ({ + maintenanceEntriesProperties: createmaintenanceEntriesProperties(state), +}); + +const mapDispatcher = (dispatcher: IDispatcher) => ({ + maintenanceEntriesActions: createmaintenanceEntriesActions(dispatcher.dispatch), + onLoadMaintenanceEntries: async () => { + await dispatcher.dispatch(maintenanceEntriesReloadAction); + }, +}); + +const emptyMaintenenceEntry: MaintenanceEntry = { + mId: '', + nodeId: '', + description: '', + start: convertToLocaleString(new Date().valueOf()), + end: convertToLocaleString(new Date().valueOf()), + active: false, +}; + +type MaintenanceViewComponentProps = Connect<typeof mapProps, typeof mapDispatcher> & WithStyles<typeof styles> & {}; + +type MaintenenceViewComponentState = { + maintenenceEntryToEdit: MaintenanceEntry; + maintenanceEntryEditorMode: EditMaintenenceEntryDialogMode; + refreshMaintenenceEntriesEditorMode: RefreshMaintenanceEntriesDialogMode; +}; + +let initialSorted = false; + +class MaintenenceViewComponent extends React.Component<MaintenanceViewComponentProps, MaintenenceViewComponentState> { + + constructor(props: MaintenanceViewComponentProps) { + super(props); + + this.state = { + maintenenceEntryToEdit: emptyMaintenenceEntry, + maintenanceEntryEditorMode: EditMaintenenceEntryDialogMode.None, + refreshMaintenenceEntriesEditorMode: RefreshMaintenanceEntriesDialogMode.None, + }; + + } + + getContextMenu(rowData: MaintenanceEntry): JSX.Element[] { + let buttonArray = [ + <MenuItem aria-label={'1hr-from-now'} onClick={event => this.onOpenPlus1hEditMaintenenceEntryDialog(event, rowData)}><Typography>+1h</Typography></MenuItem>, + <MenuItem aria-label={'8hr-from-now'} onClick={event => this.onOpenPlus8hEditMaintenenceEntryDialog(event, rowData)}><Typography>+8h</Typography></MenuItem>, + <Divider />, + <MenuItem aria-label={'edit'} onClick={event => this.onOpenEditMaintenenceEntryDialog(event, rowData)}><EditIcon /><Typography>Edit</Typography></MenuItem>, + <MenuItem aria-label={'remove'} onClick={event => this.onOpenRemoveMaintenenceEntryDialog(event, rowData)}><RemoveIcon /><Typography>Remove</Typography></MenuItem>, + ]; + return buttonArray; + } + + render() { + const addMaintenenceEntryAction = { + icon: AddIcon, tooltip: 'Add', ariaLabel:'add-element', onClick: () => { + const startTime = (new Date().valueOf()); + const endTime = startTime; + this.setState({ + maintenenceEntryToEdit: { + ...emptyMaintenenceEntry, + start: convertToLocaleString(startTime), + end: convertToLocaleString(endTime), + }, + maintenanceEntryEditorMode: EditMaintenenceEntryDialogMode.AddMaintenenceEntry, + }); + }, + }; + + const refreshMaintenanceEntriesAction = { + icon: Refresh, tooltip: 'Refresh Maintenance Entries', ariaLabel: 'refresh', onClick: () => { + this.setState({ + refreshMaintenenceEntriesEditorMode: RefreshMaintenanceEntriesDialogMode.RefreshMaintenanceEntriesTable, + }); + }, + }; + + const now = new Date().valueOf(); + return ( + <> + <MaintenanceEntriesTable stickyHeader tableId="maintenance-table" title={'Maintenance'} customActionButtons={[refreshMaintenanceEntriesAction, addMaintenenceEntryAction]} columns={ + [ + { property: 'nodeId', title: 'Node Name', type: ColumnType.text }, + { + property: 'notifications', title: 'Notification', width: 50, align: 'center', type: ColumnType.custom, customControl: ({ rowData }) => ( + rowData.active && (Date.parse(rowData.start).valueOf() <= now) && (Date.parse(rowData.end).valueOf() >= now) && <FontAwesomeIcon icon={faBan} /> || null + ), + }, + { property: 'active', title: 'Activation State', type: ColumnType.boolean, labels: { 'true': 'active', 'false': 'not active' } }, + { property: 'start', title: 'Start Date (UTC)', type: ColumnType.text }, + { property: 'end', title: 'End Date (UTC)', type: ColumnType.text }, + ] + } idProperty={'mId'}{...this.props.maintenanceEntriesActions} {...this.props.maintenanceEntriesProperties} asynchronus createContextMenu={rowData => { + return this.getContextMenu(rowData); + }} > + </MaintenanceEntriesTable> + <EditMaintenenceEntryDialog initialMaintenenceEntry={this.state.maintenenceEntryToEdit} mode={this.state.maintenanceEntryEditorMode} + onClose={this.onCloseEditMaintenenceEntryDialog} /> + <RefreshMaintenanceEntriesDialog mode={this.state.refreshMaintenenceEntriesEditorMode} + onClose={this.onCloseRefreshMaintenenceEntryDialog} /> + </> + ); + } + + public componentDidMount() { + + if (!initialSorted) { + initialSorted = true; + this.props.maintenanceEntriesActions.onHandleRequestSort('node-id'); + } else { + this.props.onLoadMaintenanceEntries(); + } + + + } + + private onOpenPlus1hEditMaintenenceEntryDialog = (event: React.MouseEvent<HTMLElement>, entry: MaintenanceEntry) => { + // event.preventDefault(); + // event.stopPropagation(); + const startTime = (new Date().valueOf()); + const endTime = startTime + (1 * 60 * 60 * 1000); + this.setState({ + maintenenceEntryToEdit: { + ...entry, + start: convertToLocaleString(startTime), + end: convertToLocaleString(endTime), + }, + maintenanceEntryEditorMode: EditMaintenenceEntryDialogMode.EditMaintenenceEntry, + }); + }; + + private onOpenPlus8hEditMaintenenceEntryDialog = (event: React.MouseEvent<HTMLElement>, entry: MaintenanceEntry) => { + // event.preventDefault(); + // event.stopPropagation(); + const startTime = (new Date().valueOf()); + const endTime = startTime + (8 * 60 * 60 * 1000); + this.setState({ + maintenenceEntryToEdit: { + ...entry, + start: convertToLocaleString(startTime), + end: convertToLocaleString(endTime), + }, + maintenanceEntryEditorMode: EditMaintenenceEntryDialogMode.EditMaintenenceEntry, + }); + }; + + private onOpenEditMaintenenceEntryDialog = (event: React.MouseEvent<HTMLElement>, entry: MaintenanceEntry) => { + // event.preventDefault(); + // event.stopPropagation(); + const startTime = (new Date().valueOf()); + const endTime = startTime; + this.setState({ + maintenenceEntryToEdit: { + ...entry, + ...(entry.start && endTime ? { start: convertToLocaleString(entry.start), end: convertToLocaleString(entry.end) } : { start: convertToLocaleString(startTime), end: convertToLocaleString(endTime) }), + }, + maintenanceEntryEditorMode: EditMaintenenceEntryDialogMode.EditMaintenenceEntry, + }); + }; + + private onOpenRemoveMaintenenceEntryDialog = (event: React.MouseEvent<HTMLElement>, entry: MaintenanceEntry) => { + // event.preventDefault(); + // event.stopPropagation(); + const startTime = (new Date().valueOf()); + const endTime = startTime; + this.setState({ + maintenenceEntryToEdit: { + ...entry, + ...(entry.start && endTime ? { start: convertToLocaleString(entry.start), end: convertToLocaleString(entry.end) } : { start: convertToLocaleString(startTime), end: convertToLocaleString(endTime) }), + }, + maintenanceEntryEditorMode: EditMaintenenceEntryDialogMode.RemoveMaintenenceEntry, + }); + }; + + private onCloseEditMaintenenceEntryDialog = () => { + this.setState({ + maintenenceEntryToEdit: emptyMaintenenceEntry, + maintenanceEntryEditorMode: EditMaintenenceEntryDialogMode.None, + }); + }; + + private onCloseRefreshMaintenenceEntryDialog = () => { + this.setState({ + refreshMaintenenceEntriesEditorMode: RefreshMaintenanceEntriesDialogMode.None, + }); + }; +} + +export const MaintenanceView = withStyles(styles)(connect(mapProps, mapDispatcher)(MaintenenceViewComponent)); + diff --git a/sdnr/wt-odlux/odlux/apps/maintenanceApp/tsconfig.json b/sdnr/wt-odlux/odlux/apps/maintenanceApp/tsconfig.json new file mode 100644 index 000000000..ca65092e0 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/maintenanceApp/tsconfig.json @@ -0,0 +1,37 @@ +{ + "compilerOptions": { + "baseUrl": "./src", + "outDir": "./dist", + "sourceMap": true, + "forceConsistentCasingInFileNames": true, + "allowSyntheticDefaultImports": true, + "allowUnreachableCode": false, + "allowUnusedLabels": false, + "noFallthroughCasesInSwitch": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "strictNullChecks": true, + "pretty": true, + "newLine": "LF", + "module": "es2015", + "target": "es2016", + "moduleResolution": "node", + "experimentalDecorators": true, + "jsx": "preserve", + "lib": [ + "dom", + "es2015", + "es2016" + ], + "types": [ + "prop-types", + "react", + "react-dom" + ] + }, + "exclude": [ + "dist", + "node_modules" + ] +} diff --git a/sdnr/wt-odlux/odlux/apps/maintenanceApp/webpack.config.js b/sdnr/wt-odlux/odlux/apps/maintenanceApp/webpack.config.js new file mode 100644 index 000000000..845fc43a6 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/maintenanceApp/webpack.config.js @@ -0,0 +1,168 @@ +/** + * Webpack 4 configuration file + * see https://webpack.js.org/configuration/ + * see https://webpack.js.org/configuration/dev-server/ + */ + +"use strict"; + +const path = require("path"); +const webpack = require("webpack"); +const CopyWebpackPlugin = require("copy-webpack-plugin"); +const TerserPlugin = require('terser-webpack-plugin'); + +// const __dirname = (path => path.replace(/^([a-z]\:)/, c => c.toUpperCase()))(process.__dirname()); + +module.exports = (env) => { + const distPath = path.resolve(__dirname, env === "release" ? "." : "../..", "dist"); + const frameworkPath = path.resolve(__dirname, env === "release" ? "../../framework" : "../..", "dist"); + return [{ + name: "App", + + mode: "none", //disable default behavior + + target: "web", + + context: path.resolve(__dirname, "src"), + + entry: { + maintenanceApp: ["./pluginMaintenance.tsx"] + }, + + devtool: env === "release" ? false : "source-map", + + resolve: { + extensions: [".ts", ".tsx", ".js", ".jsx"] + }, + + output: { + path: distPath, + filename: "[name].js", + library: "[name]", + libraryTarget: "umd2", + chunkFilename: "[name].js" + }, + module: { + rules: [{ + test: /\.tsx?$/, + exclude: /node_modules/, + use: [{ + loader: "babel-loader" + }, { + loader: "ts-loader" + }] + }, { + test: /\.jsx?$/, + exclude: /node_modules/, + use: [{ + loader: "babel-loader" + }] + }, { + //don't minify images + test: /\.(png|gif|jpg|svg)$/, + use: [{ + loader: 'url-loader', + options: { + limit: 10, + name: './images/[name].[ext]' + } + }] + }] + }, + + optimization: { + noEmitOnErrors: true, + namedModules: env !== "release", + minimize: env === "release", + minimizer: env !== "release" ? [] : [new TerserPlugin({ + terserOptions: { + warnings: false, // false, true, "verbose" + compress: { + drop_console: true, + drop_debugger: true, + } + } + })], + }, + + plugins: [ + new webpack.DllReferencePlugin({ + context: path.resolve(__dirname, "../../framework/src"), + manifest: require(path.resolve(frameworkPath, "vendor-manifest.json")), + sourceType: "umd2" + }), + new webpack.DllReferencePlugin({ + context: path.resolve(__dirname, "../../framework/src"), + manifest: require(path.resolve(frameworkPath, "app-manifest.json")), + sourceType: "umd2" + }), + ...(env === "release" ? [ + new webpack.DefinePlugin({ + "process.env": { + NODE_ENV: "'production'", + VERSION: JSON.stringify(require("./package.json").version) + } + }), + ] : [ + new webpack.DefinePlugin({ + "process.env": { + NODE_ENV: "'development'", + VERSION: JSON.stringify(require("./package.json").version) + } + }), + new CopyWebpackPlugin([{ + from: 'index.html', + to: distPath + }]), + ]) + ], + + devServer: { + public: "http://localhost:3100", + contentBase: frameworkPath, + + compress: true, + headers: { + "Access-Control-Allow-Origin": "*" + }, + host: "0.0.0.0", + port: 3100, + disableHostCheck: true, + historyApiFallback: true, + inline: true, + hot: false, + quiet: false, + stats: { + colors: true + }, + proxy: { + "/oauth2/": { + target: "http://10.20.6.29:48181", + secure: false + }, + "/database/": { + target: "http://10.20.6.29:48181", + secure: false + }, + "/restconf/": { + target: "http://10.20.6.29:48181", + secure: false + }, + "/rests/": { + target: "http://10.20.6.29:48181", + secure: false + }, + "/help/": { + target: "http://10.20.6.29:48181", + secure: false + }, + "/websocket": { + target: "http://10.20.6.29:48181", + ws: true, + changeOrigin: true, + secure: false + } + } + } + }]; +} |