summaryrefslogtreecommitdiffstats
path: root/sdnr/wt/odlux/apps/maintenanceApp/src
diff options
context:
space:
mode:
Diffstat (limited to 'sdnr/wt/odlux/apps/maintenanceApp/src')
-rw-r--r--sdnr/wt/odlux/apps/maintenanceApp/src/actions/maintenenceActions.ts79
-rw-r--r--sdnr/wt/odlux/apps/maintenanceApp/src/components/editMaintenenceEntryDialog.tsx194
-rw-r--r--sdnr/wt/odlux/apps/maintenanceApp/src/handlers/maintenanceAppRootHandler.ts41
-rw-r--r--sdnr/wt/odlux/apps/maintenanceApp/src/handlers/maintenenceEntriesHandler.ts35
-rw-r--r--sdnr/wt/odlux/apps/maintenanceApp/src/index.html26
-rw-r--r--sdnr/wt/odlux/apps/maintenanceApp/src/models/maintenenceEntryType.ts33
-rw-r--r--sdnr/wt/odlux/apps/maintenanceApp/src/pluginMaintenance.tsx44
-rw-r--r--sdnr/wt/odlux/apps/maintenanceApp/src/services/maintenenceService.ts72
-rw-r--r--sdnr/wt/odlux/apps/maintenanceApp/src/utils/timeUtils.ts45
-rw-r--r--sdnr/wt/odlux/apps/maintenanceApp/src/views/maintenenceView.tsx226
10 files changed, 795 insertions, 0 deletions
diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src/actions/maintenenceActions.ts b/sdnr/wt/odlux/apps/maintenanceApp/src/actions/maintenenceActions.ts
new file mode 100644
index 000000000..162d94367
--- /dev/null
+++ b/sdnr/wt/odlux/apps/maintenanceApp/src/actions/maintenenceActions.ts
@@ -0,0 +1,79 @@
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ */
+import { Action } from '../../../../framework/src/flux/action';
+import { Dispatch } from '../../../../framework/src/flux/store';
+import { MaintenenceEntry, spoofSymbol } from '../models/maintenenceEntryType';
+
+import { AddSnackbarNotification } from '../../../../framework/src/actions/snackbarActions';
+import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
+
+
+import { maintenenceService } from '../services/maintenenceService';
+import { maintenanceEntriesReloadAction } from '../handlers/maintenenceEntriesHandler';
+
+export class BaseAction extends Action { }
+
+export class LoadAllMainteneceEntriesAction extends BaseAction { }
+
+export class AllMainteneceEntriesLoadedAction extends BaseAction {
+
+ constructor (public maintenenceEntries: MaintenenceEntry[] | null, error?:string) {
+ super();
+
+ }
+}
+
+
+export class UpdateMaintenanceEntry extends BaseAction {
+ constructor(public maintenenceEntry: MaintenenceEntry) {
+ super();
+ }
+}
+
+/** Represents an async thunk action creator to add an element to the maintenence entries. */
+export const addOrUpdateMaintenenceEntryAsyncActionCreator = (entry: MaintenenceEntry) => (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: MaintenenceEntry) => (dispatch: Dispatch) => {
+ maintenenceService.deleteMaintenenceEntry(entry).then(result => {
+ result && window.setTimeout(() => {
+ dispatch(new UpdateMaintenanceEntry({
+ [spoofSymbol]: true,
+ _id: entry._id,
+ 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/apps/maintenanceApp/src/components/editMaintenenceEntryDialog.tsx b/sdnr/wt/odlux/apps/maintenanceApp/src/components/editMaintenenceEntryDialog.tsx
new file mode 100644
index 000000000..e0cd51493
--- /dev/null
+++ b/sdnr/wt/odlux/apps/maintenanceApp/src/components/editMaintenenceEntryDialog.tsx
@@ -0,0 +1,194 @@
+/**
+ * ============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 '@material-ui/core/Button';
+import TextField from '@material-ui/core/TextField';
+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 { IDispatcher, connect, Connect } from '../../../../framework/src/flux/connect';
+
+import {
+ addOrUpdateMaintenenceEntryAsyncActionCreator,
+ removeFromMaintenenceEntrysAsyncActionCreator
+} from '../actions/maintenenceActions';
+
+import { MaintenenceEntry } from '../models/maintenenceEntryType';
+import { FormControl, InputLabel, Select, MenuItem } from '@material-ui/core';
+
+export enum EditMaintenenceEntryDialogMode {
+ None = "none",
+ AddMaintenenceEntry = "addMaintenenceEntry",
+ EditMaintenenceEntry = "editMaintenenceEntry",
+ RemoveMaintenenceEntry = "removeMaintenenceEntry"
+}
+
+const mapDispatch = (dispatcher: IDispatcher) => ({
+ addOrUpdateMaintenenceEntry: (entry: MaintenenceEntry) => {
+ dispatcher.dispatch(addOrUpdateMaintenenceEntryAsyncActionCreator(entry));
+ },
+ removeMaintenenceEntry: (entry: MaintenenceEntry) => {
+ 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: MaintenenceEntry;
+ onClose: () => void;
+};
+
+type EditMaintenenceEntryDIalogComponentState = MaintenenceEntry;
+
+class EditMaintenenceEntryDIalogComponent extends React.Component<EditMaintenenceEntryDIalogComponentProps, EditMaintenenceEntryDIalogComponentState> {
+ constructor (props: EditMaintenenceEntryDIalogComponentProps) {
+ super(props);
+
+ this.state = {
+ ...this.props.initialMaintenenceEntry
+ };
+ }
+
+ 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 disabled={!setting.enableMountIdEditor} spellCheck={false} autoFocus margin="dense" id="name" label="Name" type="text" fullWidth value={this.state.nodeId} onChange={(event) => { this.setState({ nodeId: event.target.value }); }} />
+ <TextField 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) => {
+ 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) => {
+ 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
+ });
+ event.preventDefault();
+ event.stopPropagation();
+ }} > {setting.applyButtonText} </Button>
+ <Button onClick={(event) => {
+ this.onCancel();
+ event.preventDefault();
+ event.stopPropagation();
+ }} color="secondary"> {setting.cancelButtonText} </Button>
+ </DialogActions>
+ </Dialog>
+ )
+ }
+
+ private onApply = (entry: MaintenenceEntry) => {
+ 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 & { _initialMaintenenceEntry: MaintenenceEntry }): EditMaintenenceEntryDIalogComponentState & { _initialMaintenenceEntry: MaintenenceEntry } {
+ if (props.initialMaintenenceEntry !== state._initialMaintenenceEntry) {
+ state = {
+ ...state,
+ ...props.initialMaintenenceEntry,
+ _initialMaintenenceEntry: 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/apps/maintenanceApp/src/handlers/maintenanceAppRootHandler.ts b/sdnr/wt/odlux/apps/maintenanceApp/src/handlers/maintenanceAppRootHandler.ts
new file mode 100644
index 000000000..71b4bf70d
--- /dev/null
+++ b/sdnr/wt/odlux/apps/maintenanceApp/src/handlers/maintenanceAppRootHandler.ts
@@ -0,0 +1,41 @@
+/**
+ * ============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 { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
+
+import { IMaintenanceEntriesState, maintenanceEntriesActionHandler } from './maintenenceEntriesHandler';
+
+export interface IMaintenanceAppStoreState {
+ maintenenceEntries : IMaintenanceEntriesState
+}
+
+declare module '../../../../framework/src/store/applicationStore' {
+ interface IApplicationStoreState {
+ maintenance: IMaintenanceAppStoreState
+ }
+}
+
+const actionHandlers = {
+ maintenenceEntries: maintenanceEntriesActionHandler
+};
+
+export const maintenanceAppRootHandler = combineActionHandler<IMaintenanceAppStoreState>(actionHandlers);
+export default maintenanceAppRootHandler;
diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src/handlers/maintenenceEntriesHandler.ts b/sdnr/wt/odlux/apps/maintenanceApp/src/handlers/maintenenceEntriesHandler.ts
new file mode 100644
index 000000000..445541889
--- /dev/null
+++ b/sdnr/wt/odlux/apps/maintenanceApp/src/handlers/maintenenceEntriesHandler.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 { MaintenenceEntry } from '../models/maintenenceEntryType';
+export interface IMaintenanceEntriesState extends IExternalTableState<MaintenenceEntry> { }
+
+// create eleactic search material data fetch handler
+const maintenanceEntriesSearchHandler = createSearchDataHandler<MaintenenceEntry>('maintenance');
+
+export const {
+ actionHandler: maintenanceEntriesActionHandler,
+ createActions: createmaintenanceEntriesActions,
+ createProperties: createmaintenanceEntriesProperties,
+ reloadAction: maintenanceEntriesReloadAction,
+
+ // set value action, to change a value
+} = createExternal<MaintenenceEntry>(maintenanceEntriesSearchHandler, appState => appState.maintenance.maintenenceEntries);
+
diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src/index.html b/sdnr/wt/odlux/apps/maintenanceApp/src/index.html
new file mode 100644
index 000000000..c84aecee1
--- /dev/null
+++ b/sdnr/wt/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/apps/maintenanceApp/src/models/maintenenceEntryType.ts b/sdnr/wt/odlux/apps/maintenanceApp/src/models/maintenenceEntryType.ts
new file mode 100644
index 000000000..989bfbdc3
--- /dev/null
+++ b/sdnr/wt/odlux/apps/maintenanceApp/src/models/maintenenceEntryType.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 MaintenenceEntry = {
+ _id: string;
+ nodeId: string;
+ description?: string,
+ end: string,
+ start: string
+ active: boolean
+ [spoofSymbol]?: boolean;
+}
+
diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src/pluginMaintenance.tsx b/sdnr/wt/odlux/apps/maintenanceApp/src/pluginMaintenance.tsx
new file mode 100644
index 000000000..e6ab977b3
--- /dev/null
+++ b/sdnr/wt/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 * as React from "react";
+
+import { faLock } from '@fortawesome/free-solid-svg-icons'; // select app icon
+
+import applicationManager from '../../../framework/src/services/applicationManager';
+
+import { maintenanceAppRootHandler } from './handlers/maintenanceAppRootHandler';
+
+import MaintenenceView from "./views/maintenenceView";
+
+const App : React.SFC = (props) => {
+ return <MaintenenceView />
+};
+
+export function register() {
+ applicationManager.registerApplication({
+ name: "maintenance",
+ icon: faLock,
+ rootComponent: App,
+ rootActionHandler: maintenanceAppRootHandler,
+ menuEntry: "Maintenance"
+ });
+}
+
+
diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src/services/maintenenceService.ts b/sdnr/wt/odlux/apps/maintenanceApp/src/services/maintenenceService.ts
new file mode 100644
index 000000000..daa6082d7
--- /dev/null
+++ b/sdnr/wt/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 { requestRest } from '../../../../framework/src/services/restService';
+import { Result, HitEntry, PostResponse, DeleteResponse } from '../../../../framework/src/models/elasticSearch';
+
+import { MaintenenceEntry } from '../models/maintenenceEntryType';
+import { convertToLocaleString, convertToGMTString, convertToISODateString } from '../utils/timeUtils';
+import { convertPropertyNames, replaceUpperCase } from '../../../../framework/src/utilities/yangHelper';
+
+
+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: MaintenenceEntry): Promise<PostResponse | null> {
+ const path = `/restconf/operations/data-provider:create-maintenance`;
+
+ const query = {
+ "id": maintenenceEntry._id,
+ "node-id": maintenenceEntry.nodeId,
+ "active": maintenenceEntry.active,
+ "description": maintenenceEntry.description,
+ "end": convertToISODateString(maintenenceEntry.end),
+ "start": convertToISODateString(maintenenceEntry.start)
+ };
+
+ const result = await requestRest<PostResponse>(path, { method: "POST", body: JSON.stringify(convertPropertyNames({ input: query }, replaceUpperCase)) });
+ return result || null;
+ }
+
+ /**
+ * Deletes one maintenence entry by its mountId from the backend.
+ */
+ public async deleteMaintenenceEntry(maintenenceEntry: MaintenenceEntry): Promise<(DeleteResponse) | null> {
+ const path = `/restconf/operations/data-provider:delete-maintenance`;
+
+ const query = {
+ "id": maintenenceEntry._id,
+ "node-id": maintenenceEntry.nodeId,
+ "active": maintenenceEntry.active,
+ "description": maintenenceEntry.description,
+ "end": convertToISODateString(maintenenceEntry.end),
+ "start": convertToISODateString(maintenenceEntry.start)
+ };
+ const result = await requestRest<DeleteResponse>(path, { method: "POST", body: JSON.stringify(convertPropertyNames({ 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/apps/maintenanceApp/src/utils/timeUtils.ts b/sdnr/wt/odlux/apps/maintenanceApp/src/utils/timeUtils.ts
new file mode 100644
index 000000000..676be1a38
--- /dev/null
+++ b/sdnr/wt/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/apps/maintenanceApp/src/views/maintenenceView.tsx b/sdnr/wt/odlux/apps/maintenanceApp/src/views/maintenenceView.tsx
new file mode 100644
index 000000000..3b7879351
--- /dev/null
+++ b/sdnr/wt/odlux/apps/maintenanceApp/src/views/maintenenceView.tsx
@@ -0,0 +1,226 @@
+/**
+ * ============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 { Theme, createStyles, WithStyles, withStyles, Tooltip } from '@material-ui/core';
+
+import { faBan } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+
+import AddIcon from '@material-ui/icons/Add';
+import EditIcon from '@material-ui/icons/Edit';
+import RemoveIcon from '@material-ui/icons/RemoveCircleOutline';
+
+import Button from '@material-ui/core/Button';
+import IconButton from '@material-ui/core/IconButton';
+
+import connect, { IDispatcher, Connect } from '../../../../framework/src/flux/connect';
+import MaterialTable, { MaterialTableCtorType, ColumnType } from '../../../../framework/src/components/material-table';
+import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
+
+import { MaintenenceEntry, spoofSymbol } from '../models/maintenenceEntryType';
+
+import EditMaintenenceEntryDialog, { EditMaintenenceEntryDialogMode } from '../components/editMaintenenceEntryDialog';
+import { convertToLocaleString } from '../utils/timeUtils';
+import { createmaintenanceEntriesActions, createmaintenanceEntriesProperties, maintenanceEntriesReloadAction } from '../handlers/maintenenceEntriesHandler';
+
+const styles = (theme: Theme) => createStyles({
+ button: {
+ margin: 0,
+ padding: "6px 6px",
+ minWidth: 'unset'
+ },
+ spacer: {
+ marginLeft: theme.spacing(1),
+ marginRight: theme.spacing(1),
+ display: "inline"
+ }
+});
+
+const MaintenenceEntriesTable = MaterialTable as MaterialTableCtorType<MaintenenceEntry>;
+
+const mapProps = (state: IApplicationStoreState) => ({
+ maintenanceEntriesProperties: createmaintenanceEntriesProperties(state)
+});
+
+const mapDispatcher = (dispatcher: IDispatcher) => ({
+ maintenanceEntriesActions: createmaintenanceEntriesActions(dispatcher.dispatch),
+ onLoadMaintenanceEntries: async () => {
+ await dispatcher.dispatch(maintenanceEntriesReloadAction)
+ }
+});
+
+const emptyMaintenenceEntry: MaintenenceEntry = {
+ _id: '',
+ nodeId: '',
+ description: '',
+ start: convertToLocaleString(new Date().valueOf()),
+ end: convertToLocaleString(new Date().valueOf()),
+ active: false,
+};
+
+type MaintenenceViewComponentProps = Connect<typeof mapProps, typeof mapDispatcher> & WithStyles<typeof styles> & {
+
+}
+
+type MaintenenceViewComponentState = {
+ maintenenceEntryToEdit: MaintenenceEntry;
+ maintenenceEntryEditorMode: EditMaintenenceEntryDialogMode;
+}
+
+class MaintenenceViewComponent extends React.Component<MaintenenceViewComponentProps, MaintenenceViewComponentState> {
+
+ constructor(props: MaintenenceViewComponentProps) {
+ super(props);
+
+ this.state = {
+ maintenenceEntryToEdit: emptyMaintenenceEntry,
+ maintenenceEntryEditorMode: EditMaintenenceEntryDialogMode.None,
+ };
+
+ }
+
+ render() {
+ const { classes } = this.props;
+ const addMaintenenceEntryAction = {
+ icon: AddIcon, tooltip: 'Add', onClick: () => {
+ const startTime = (new Date().valueOf());
+ const endTime = startTime;
+ this.setState({
+ maintenenceEntryToEdit: {
+ ...emptyMaintenenceEntry,
+ start: convertToLocaleString(startTime),
+ end: convertToLocaleString(endTime),
+ },
+ maintenenceEntryEditorMode: EditMaintenenceEntryDialogMode.AddMaintenenceEntry
+ });
+ }
+ };
+ const now = new Date().valueOf();
+ return (
+ <>
+ <MaintenenceEntriesTable title={"Maintenance"} customActionButtons={[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 },
+ {
+ property: "actions", title: "Actions", type: ColumnType.custom, customControl: ({ rowData }) => (
+ <>
+ <div className={classes.spacer}>
+ <Tooltip title={"1h from now"} ><Button className={classes.button} onClick={(event) => this.onOpenPlus1hEditMaintenenceEntryDialog(event, rowData)} >+1h</Button></Tooltip>
+ <Tooltip title={"8h from now"} ><Button className={classes.button} onClick={(event) => this.onOpenPlus8hEditMaintenenceEntryDialog(event, rowData)} >+8h</Button></Tooltip>
+ </div>
+ <div className={classes.spacer}>
+ <Tooltip title={"Edit"} ><IconButton className={classes.button} onClick={(event) => this.onOpenEditMaintenenceEntryDialog(event, rowData)} ><EditIcon /></IconButton></Tooltip>
+ <Tooltip title={"Remove"} ><IconButton disabled={!!rowData[spoofSymbol]} className={classes.button} onClick={(event) => this.onOpenRemoveMaintenenceEntryDialog(event, rowData)} ><RemoveIcon /></IconButton></Tooltip>
+ </div>
+ </>
+ )
+ },
+ ]
+ } idProperty={'_id'}{...this.props.maintenanceEntriesActions} {...this.props.maintenanceEntriesProperties} asynchronus > </MaintenenceEntriesTable>
+ <EditMaintenenceEntryDialog initialMaintenenceEntry={this.state.maintenenceEntryToEdit} mode={this.state.maintenenceEntryEditorMode}
+ onClose={this.onCloseEditMaintenenceEntryDialog} />
+ </>
+ );
+ }
+
+ public componentDidMount() {
+ this.props.maintenanceEntriesActions.onRefresh();
+ this.props.onLoadMaintenanceEntries();
+ }
+
+ private onOpenPlus1hEditMaintenenceEntryDialog = (event: React.MouseEvent<HTMLElement>, entry: MaintenenceEntry) => {
+ 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),
+ },
+ maintenenceEntryEditorMode: EditMaintenenceEntryDialogMode.EditMaintenenceEntry
+ });
+ }
+
+ private onOpenPlus8hEditMaintenenceEntryDialog = (event: React.MouseEvent<HTMLElement>, entry: MaintenenceEntry) => {
+ 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),
+ },
+ maintenenceEntryEditorMode: EditMaintenenceEntryDialogMode.EditMaintenenceEntry
+ });
+ }
+
+ private onOpenEditMaintenenceEntryDialog = (event: React.MouseEvent<HTMLElement>, entry: MaintenenceEntry) => {
+ 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) }
+ },
+ maintenenceEntryEditorMode: EditMaintenenceEntryDialogMode.EditMaintenenceEntry
+ });
+ }
+
+ private onOpenRemoveMaintenenceEntryDialog = (event: React.MouseEvent<HTMLElement>, entry: MaintenenceEntry) => {
+ 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) }
+ },
+ maintenenceEntryEditorMode: EditMaintenenceEntryDialogMode.RemoveMaintenenceEntry
+ });
+ }
+
+ private onCloseEditMaintenenceEntryDialog = () => {
+ this.setState({
+ maintenenceEntryToEdit: emptyMaintenenceEntry,
+ maintenenceEntryEditorMode: EditMaintenenceEntryDialogMode.None,
+ });
+ }
+}
+
+export const MaintenenceView = withStyles(styles)(connect(mapProps, mapDispatcher)(MaintenenceViewComponent));
+export default MaintenenceView; \ No newline at end of file