aboutsummaryrefslogtreecommitdiffstats
path: root/sdnr/wt-odlux/odlux/framework/src
diff options
context:
space:
mode:
Diffstat (limited to 'sdnr/wt-odlux/odlux/framework/src')
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/actions/authentication.ts107
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/actions/errorActions.ts42
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/actions/loginProvider.ts36
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/actions/menuAction.ts31
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/actions/navigationActions.ts64
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/actions/settingsAction.ts130
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/actions/snackbarActions.ts37
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/actions/titleActions.ts27
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/actions/websocketAction.ts26
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/app.css17
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/app.tsx120
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/assets/icons/About.svg18
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/assets/icons/Home.svg28
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/assets/icons/Menu.svg18
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/assets/icons/Tools.svg35
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/assets/icons/User.svg21
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/assets/icons/ht.Connect.pngbin0 -> 14989 bytes
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/assets/icons/ht.Connect.svg81
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/assets/images/defaultLogo.svg179
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/assets/images/defaultLogo.svg.d.ts19
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/assets/images/home.svg20
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/assets/images/home.svg.d.ts20
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/assets/images/odluxLogo.gifbin0 -> 5709 bytes
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/assets/images/odluxLogo.gif.d.ts20
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/assets/images/onapLogo.gifbin0 -> 9249 bytes
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/assets/images/onapLogo.gif.d.ts20
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/assets/version.json4
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/common/event.ts81
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/components/errorDisplay.tsx131
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/components/icons/menuIcon.tsx29
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/components/logo.tsx103
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/components/material-table/columnModel.ts56
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/components/material-table/index.tsx707
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/components/material-table/showColumnDialog.tsx188
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/components/material-table/tableFilter.tsx113
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/components/material-table/tableHead.tsx127
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/components/material-table/tableToolbar.tsx191
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/components/material-table/utilities.ts357
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/components/material-ui/index.ts22
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/components/material-ui/listItemLink.tsx83
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/components/material-ui/loader.tsx49
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/components/material-ui/panel.tsx76
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/components/material-ui/snackDisplay.tsx74
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/components/material-ui/toggleButton.tsx181
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/components/material-ui/toggleButtonGroup.tsx40
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/components/material-ui/treeView.tsx380
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/components/navigationMenu.tsx218
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/components/objectDump/index.tsx205
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/components/routing/appFrame.tsx55
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/components/settings/general.tsx110
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/components/titleBar.tsx233
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/design/default.ts81
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/favicon.icobin0 -> 1150 bytes
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/flux/action.ts26
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/flux/connect.tsx213
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/flux/middleware.ts107
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/flux/store.ts106
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/handlers/applicationRegistryHandler.ts31
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/handlers/applicationStateHandler.ts176
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/handlers/authenticationHandler.ts59
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/handlers/navigationStateHandler.ts45
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/index.dev.html35
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/index.html27
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/middleware/api.ts72
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/middleware/logger.ts35
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/middleware/navigation.ts96
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/middleware/policies.ts41
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/middleware/thunk.ts35
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/models/applicationConfig.ts5
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/models/applicationInfo.ts55
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/models/authentication.ts94
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/models/elasticSearch.ts72
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/models/errorInfo.ts29
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/models/externalLoginProvider.ts23
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/models/iconDefinition.ts21
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/models/index.ts18
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/models/restService.ts48
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/models/settings.ts48
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/models/snackbarItem.ts20
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/run.ts19
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/services/applicationApi.ts74
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/services/applicationManager.ts53
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/services/authenticationService.ts96
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/services/broadcastService.ts115
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/services/index.ts22
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/services/notificationService.ts220
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/services/restAccessorService.ts93
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/services/restService.ts168
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/services/snackbarService.ts22
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/services/storeService.ts11
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/services/userSessionService.ts80
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/services/userdataService.ts28
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/store/applicationStore.ts74
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/styles/att.ts46
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/utilities/elasticSearch.ts114
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/utilities/logLevel.ts8
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/utilities/withComponents.ts37
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/utilities/yangHelper.ts44
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/views/about.tsx196
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/views/frame.tsx134
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/views/home.tsx59
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/views/login.tsx250
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/views/settings.tsx115
-rw-r--r--sdnr/wt-odlux/odlux/framework/src/views/test.tsx877
104 files changed, 9502 insertions, 0 deletions
diff --git a/sdnr/wt-odlux/odlux/framework/src/actions/authentication.ts b/sdnr/wt-odlux/odlux/framework/src/actions/authentication.ts
new file mode 100644
index 000000000..141249530
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/actions/authentication.ts
@@ -0,0 +1,107 @@
+/**
+ * ============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 { Dispatch } from '../flux/store';
+import { Action } from '../flux/action';
+import { AuthPolicy, User } from '../models/authentication';
+import { Settings } from '../models/settings';
+import { saveInitialSettings, SetGeneralSettingsAction } from './settingsAction';
+import { endWebsocketSession } from '../services/notificationService';
+import { endUserSession, startUserSession } from '../services/userSessionService';
+import { IApplicationStoreState } from '../store/applicationStore';
+
+export class UpdateUser extends Action {
+
+ constructor(public user?: User) {
+ super();
+ }
+}
+
+export class UpdatePolicies extends Action {
+
+ constructor(public authPolicies?: AuthPolicy[]) {
+ super();
+ }
+}
+
+export const logoutUser = () => (dispatcher: Dispatch, getState: () => IApplicationStoreState) =>{
+
+ const { framework:{ applicationState:{ authentication }, authenticationState: { user } } } = getState();
+
+ dispatcher(new UpdateUser(undefined));
+ dispatcher(new SetGeneralSettingsAction(null));
+ endWebsocketSession();
+ endUserSession();
+ localStorage.removeItem('userToken');
+
+
+ //only call if a user is currently logged in
+ if (authentication === 'oauth' && user) {
+
+ const url = window.location.origin;
+ window.location.href = `${url}/oauth/logout`;
+ }
+};
+
+/**
+ * Loads the user settings for the given user and dispatches a `saveInitialSettings` action with the result.
+ * @param user The user for which to load the settings.
+ * @param dispatcher The dispatcher function to use for dispatching the `saveInitialSettings` action.
+ */
+const loadUserSettings = (user: User | undefined, dispatcher: Dispatch) => {
+
+ // fetch used, because state change for user login is not done when frameworks restRequest call is started (and is accordingly undefined -> /userdata call yields 401, unauthorized) and triggering an action from inside the handler / login event is impossible
+ // no timeout used, because it's bad practice to add a timeout to hopefully avoid a race condition
+ // hence, fetch used to simply use supplied user data for getting settings
+
+ if (user && user.isValid) {
+
+ fetch('/userdata', {
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Accept': 'application/json',
+ 'Authorization': `${user.tokenType} ${user.token}`,
+ },
+ }).then((res: Response)=>{
+ if (res.status == 200) {
+ return res.json();
+ } else {
+ return null;
+ }
+ }).then((result:Settings)=>{
+ dispatcher(saveInitialSettings(result));
+ });
+ }
+};
+
+/**
+ * Dispatches an `UpdateUser` action with the given user and starts a user session if the user is defined.
+ * Also loads the user settings for the given user and dispatches a `saveInitialSettings` action with the result.
+ * Finally, saves the user token to local storage.
+ * @param user The user to be logged in.
+ * @param dispatcher The dispatcher function to use for dispatching the actions.
+ */
+export const loginUserAction = (user?: User) => (dispatcher: Dispatch) =>{
+
+ dispatcher(new UpdateUser(user));
+ if (user) {
+ startUserSession(user);
+ loadUserSettings(user, dispatcher);
+ localStorage.setItem('userToken', user.toString());
+ }
+}; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/actions/errorActions.ts b/sdnr/wt-odlux/odlux/framework/src/actions/errorActions.ts
new file mode 100644
index 000000000..ddb0d5728
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/actions/errorActions.ts
@@ -0,0 +1,42 @@
+/**
+ * ============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 '../flux/action';
+
+import { ErrorInfo } from '../models/errorInfo';
+export { ErrorInfo } from '../models/errorInfo';
+
+export class AddErrorInfoAction extends Action {
+
+ constructor(public errorInfo: ErrorInfo) {
+ super();
+ }
+}
+
+export class RemoveErrorInfoAction extends Action {
+
+ constructor(public errorInfo: ErrorInfo) {
+ super();
+ }
+}
+
+export class ClearErrorInfoAction extends Action {
+
+ constructor() {
+ super();
+ }
+} \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/actions/loginProvider.ts b/sdnr/wt-odlux/odlux/framework/src/actions/loginProvider.ts
new file mode 100644
index 000000000..e6467111e
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/actions/loginProvider.ts
@@ -0,0 +1,36 @@
+/**
+ * ============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 '../flux/action';
+import { Dispatch } from '../flux/store';
+
+import { IApplicationStoreState } from '../store/applicationStore';
+import { ExternalLoginProvider } from '../models/externalLoginProvider';
+
+import authenticationService from '../services/authenticationService';
+
+export class SetExternalLoginProviderAction extends Action {
+ constructor(public externalLoginProvders: ExternalLoginProvider[] | null) {
+ super();
+ }
+}
+
+export const updateExternalLoginProviderAsyncActionCreator = () => async (dispatch: Dispatch, getState: () => IApplicationStoreState ) => {
+ const providers = await authenticationService.getAvaliableExteralProvider();
+ dispatch(new SetExternalLoginProviderAction(providers || null));
+} \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/actions/menuAction.ts b/sdnr/wt-odlux/odlux/framework/src/actions/menuAction.ts
new file mode 100644
index 000000000..ec0796587
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/actions/menuAction.ts
@@ -0,0 +1,31 @@
+/**
+ * ============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 '../flux/action';
+
+export class MenuAction extends Action {
+ constructor(public isOpen: boolean) {
+ super();
+ }
+}
+
+export class MenuClosedByUser extends Action {
+ constructor(public isClosed: boolean) {
+ super();
+ }
+} \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/actions/navigationActions.ts b/sdnr/wt-odlux/odlux/framework/src/actions/navigationActions.ts
new file mode 100644
index 000000000..6951b979f
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/actions/navigationActions.ts
@@ -0,0 +1,64 @@
+/**
+ * ============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 "../flux/action";
+
+export abstract class NavigationAction extends Action { }
+
+export class NavigateToApplication<TState = { }> extends NavigationAction {
+
+ constructor(public applicationName: string, public href?: string, public state?: TState, public replace: boolean = false ) {
+ super();
+
+ }
+}
+
+export class PushAction<TState = { }> extends NavigationAction {
+ constructor(public href: string, public state?: TState) {
+ super();
+
+ }
+}
+
+export class ReplaceAction<TState = { }> extends NavigationAction {
+ constructor(public href: string, public state?: TState) {
+ super();
+
+ }
+}
+
+export class GoAction extends NavigationAction {
+ constructor(public index: number) {
+ super();
+
+ }
+}
+
+export class GoBackAction extends NavigationAction {
+
+}
+
+export class GoForwardeAction extends NavigationAction {
+
+}
+
+export class LocationChanged extends NavigationAction {
+ constructor(public pathname: string, public search: string, public hash: string ) {
+ super();
+
+ }
+} \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/actions/settingsAction.ts b/sdnr/wt-odlux/odlux/framework/src/actions/settingsAction.ts
new file mode 100644
index 000000000..fa45f239c
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/actions/settingsAction.ts
@@ -0,0 +1,130 @@
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ */
+
+import { Dispatch } from "../flux/store";
+import { Action } from "../flux/action";
+import { GeneralSettings, Settings, TableSettings, TableSettingsColumn } from "../models/settings";
+import { getUserData, saveUserData } from "../services/userdataService";
+import { startWebsocketSession, suspendWebsocketSession } from "../services/notificationService";
+import { IApplicationStoreState } from "../store/applicationStore";
+
+
+export class SetGeneralSettingsAction extends Action {
+ /**
+ *
+ */
+ constructor(public areNoticationsActive: boolean | null) {
+ super();
+ }
+}
+
+export class SetTableSettings extends Action {
+
+ constructor(public tableName: string, public updatedColumns: TableSettingsColumn[]) {
+ super();
+ }
+}
+
+export class LoadSettingsAction extends Action {
+
+ constructor(public settings: Settings & { isInitialLoadDone: true }) {
+ super();
+ }
+
+}
+
+export class SettingsDoneLoadingAction extends Action {
+
+}
+
+export const setGeneralSettingsAction = (value: boolean) => (dispatcher: Dispatch) => {
+
+ dispatcher(new SetGeneralSettingsAction(value));
+
+ if (value) {
+ startWebsocketSession();
+ } else {
+ suspendWebsocketSession();
+ }
+}
+
+
+export const updateGeneralSettingsAction = (activateNotifications: boolean) => async (dispatcher: Dispatch) => {
+
+ const value: GeneralSettings = { general: { areNotificationsEnabled: activateNotifications } };
+ const result = await saveUserData("/general", JSON.stringify(value.general));
+ dispatcher(setGeneralSettingsAction(activateNotifications));
+
+}
+
+export const updateTableSettings = (tableName: string, columns: TableSettingsColumn[]) => async (dispatcher: Dispatch, getState: () => IApplicationStoreState) => {
+
+
+ //TODO: ask micha how to handle object with variable properties!
+ //fix for now: just safe everything!
+
+ let {framework:{applicationState:{settings:{tables}}}} = getState();
+
+ tables[tableName] = { columns: columns };
+ const json=JSON.stringify(tables);
+
+ // would only save latest entry
+ //const json = JSON.stringify({ [tableName]: { columns: columns } });
+
+ const result = await saveUserData("/tables", json);
+
+ dispatcher(new SetTableSettings(tableName, columns));
+}
+
+export const getGeneralSettingsAction = () => async (dispatcher: Dispatch) => {
+
+ const result = await getUserData<GeneralSettings>();
+
+ if (result && result.general) {
+ dispatcher(new SetGeneralSettingsAction(result.general.areNotificationsEnabled!))
+ }
+}
+
+export const saveInitialSettings = (settings: any) => async (dispatcher: Dispatch) => {
+
+ const defaultSettings = {general:{ areNotificationsEnabled: false }, tables:{}};
+
+ const initialSettings = {...defaultSettings, ...settings};
+
+ if (initialSettings) {
+ if (initialSettings.general) {
+ const settingsActive = initialSettings.general.areNotificationsEnabled;
+
+ if (settingsActive) {
+ startWebsocketSession();
+ } else {
+ suspendWebsocketSession();
+ }
+ }
+
+ dispatcher(new LoadSettingsAction(initialSettings));
+ }
+ else {
+ dispatcher(new SettingsDoneLoadingAction());
+
+ }
+
+
+
+
+} \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/actions/snackbarActions.ts b/sdnr/wt-odlux/odlux/framework/src/actions/snackbarActions.ts
new file mode 100644
index 000000000..ad4d60609
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/actions/snackbarActions.ts
@@ -0,0 +1,37 @@
+/**
+ * ============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 '../flux/action';
+import { SnackbarItem } from '../models/snackbarItem';
+import { DistributiveOmit } from '@mui/types';
+
+export class AddSnackbarNotification extends Action {
+
+ constructor(notification: DistributiveOmit<SnackbarItem, 'key' >) {
+ super();
+
+ this.notification = { ...notification, key: (new Date().getTime() + Math.random()) }
+ }
+
+ public notification: SnackbarItem
+}
+
+export class RemoveSnackbarNotification extends Action {
+ constructor(public key: number) {
+ super();
+ }
+} \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/actions/titleActions.ts b/sdnr/wt-odlux/odlux/framework/src/actions/titleActions.ts
new file mode 100644
index 000000000..4bcfe292e
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/actions/titleActions.ts
@@ -0,0 +1,27 @@
+/**
+ * ============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 '../flux/action';
+
+import { IconType } from '../models/iconDefinition';
+
+export class SetTitleAction extends Action {
+
+ constructor(public title: string, public icon?: IconType, public appId?: string) {
+ super();
+ }
+} \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/actions/websocketAction.ts b/sdnr/wt-odlux/odlux/framework/src/actions/websocketAction.ts
new file mode 100644
index 000000000..0b45f7ac7
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/actions/websocketAction.ts
@@ -0,0 +1,26 @@
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ */
+
+import { Action } from "../flux/action";
+
+
+export class SetWebsocketAction extends Action {
+ constructor(public isConnected: boolean|null) {
+ super();
+ }
+} \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/app.css b/sdnr/wt-odlux/odlux/framework/src/app.css
new file mode 100644
index 000000000..9b653b3b5
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/app.css
@@ -0,0 +1,17 @@
+html, body, #app {
+ height: 100%;
+ min-width: 1000px;
+ padding: 0px;
+ margin: 0px;
+ font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
+}
+.about-table td{
+ padding:0.5rem 1rem;
+ border-bottom: 1px solid #DDD;
+}
+.about-table pre {
+ background:#FFF;
+ border:1px solid #CCC;
+ padding:1rem;
+ margin: 1rem 0;
+} \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/app.tsx b/sdnr/wt-odlux/odlux/framework/src/app.tsx
new file mode 100644
index 000000000..bbe1f9ec8
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/app.tsx
@@ -0,0 +1,120 @@
+/**
+ * ============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 * as ReactDOM from 'react-dom';
+
+import { ThemeProvider, Theme, StyledEngineProvider } from '@mui/material/styles';
+
+import { Frame } from './views/frame';
+
+import { User } from './models/authentication';
+
+import { AddErrorInfoAction } from './actions/errorActions';
+import { loginUserAction } from './actions/authentication';
+
+import { applicationStoreCreator } from './store/applicationStore';
+import { ApplicationStoreProvider } from './flux/connect';
+
+import { startHistoryListener } from './middleware/navigation';
+import { startSoreService } from './services/storeService';
+
+import { startUserSessionService } from './services/userSessionService';
+import { startNotificationService } from './services/notificationService';
+
+import { startBroadcastChannel } from './services/broadcastService';
+
+import theme from './design/default';
+import '!style-loader!css-loader!./app.css';
+
+declare module '@mui/material/styles' {
+
+ interface IDesign {
+ id: string,
+ name: string,
+ url: string, // image url of a company logo, which will be presented in the ui header
+ height: number, // image height [px] as delivered by the url
+ width: number, // image width [px] as delivered by the url
+ logoHeight: number // height in [px] of the logo (see url) within the ui header
+ }
+
+ interface Theme {
+ design?: IDesign
+ }
+ interface DeprecatedThemeOptions {
+ design?: IDesign
+ }
+}
+
+
+declare module '@mui/styles/defaultTheme' {
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface (remove this line if you don't have the rule enabled)
+ interface DefaultTheme extends Theme {}
+}
+
+export { configureApplication } from "./handlers/applicationStateHandler";
+
+export const transportPCEUrl = "transportPCEUrl";
+
+export const runApplication = () => {
+
+ const initialToken = localStorage.getItem("userToken");
+ const applicationStore = applicationStoreCreator();
+
+ startBroadcastChannel(applicationStore);
+ startUserSessionService(applicationStore);
+
+ if (initialToken) {
+ applicationStore.dispatch(loginUserAction(User.fromString(initialToken) || undefined));
+ }
+
+ window.onerror = function (msg: string, url: string, line: number, col: number, error: Error) {
+ // Note that col & error are new to the HTML 5 spec and may not be
+ // supported in every browser. It worked for me in Chrome.
+ var extra = !col ? '' : '\ncolumn: ' + col;
+ extra += !error ? '' : '\nerror: ' + error;
+
+ // You can view the information in an alert to see things working like this:
+ applicationStore.dispatch(new AddErrorInfoAction({ error, message: msg, url, line, col, info: { extra } }));
+
+ var suppressErrorAlert = true;
+ // If you return true, then error alerts (like in older versions of
+ // Internet Explorer) will be suppressed.
+ return suppressErrorAlert;
+ };
+
+
+ startSoreService(applicationStore);
+ startHistoryListener(applicationStore);
+ startNotificationService(applicationStore);
+
+ const App = (): JSX.Element => (
+ <ApplicationStoreProvider applicationStore={applicationStore} >
+ <StyledEngineProvider injectFirst>
+ <ThemeProvider theme={theme}>
+ <Frame />
+ </ThemeProvider>
+ </StyledEngineProvider>
+ </ApplicationStoreProvider>
+ );
+
+ ReactDOM.render(<App />, document.getElementById('app'));
+
+
+
+};
diff --git a/sdnr/wt-odlux/odlux/framework/src/assets/icons/About.svg b/sdnr/wt-odlux/odlux/framework/src/assets/icons/About.svg
new file mode 100644
index 000000000..156e36efe
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/assets/icons/About.svg
@@ -0,0 +1,18 @@
+<!-- highstreet technologies GmbH colour scheme
+ Grey #565656
+ LBlue #36A9E1
+ DBlue #246DA2
+ Green #003F2C / #006C4B
+ Yellw #C8D400
+ Red #D81036
+-->
+
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+
+<path fill="#36A9E1" d="M 13 13 V 17.5 A 1 1 0 0 1 11 17.5 V 13 A 1 1 0 0 1 13 13 Z "/>
+
+<path fill="#36A9E1" d="M 12 11 A 1 1 0 1 0 11 10 A 1 1 0 0 0 12 11 Z "/>
+
+<path fill="#565656" d="M 1.125 20.485 A 1 1 0 0 1 1.152 19.47 L 11.152 3.47 A 1.039 1.039 0 0 1 12.852 3.47 L 22.852 19.47 A 1 1 0 0 1 22 21 H 2 A 1 1 0 0 1 1.125 20.485 Z M 3.8 19 H 20.2 L 12 5.887 Z"/>
+
+</svg> \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/assets/icons/Home.svg b/sdnr/wt-odlux/odlux/framework/src/assets/icons/Home.svg
new file mode 100644
index 000000000..0836714b4
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/assets/icons/Home.svg
@@ -0,0 +1,28 @@
+<!-- highstreet technologies GmbH colour scheme
+ Grey #565656
+ LBlue #36A9E1
+ DBlue #246DA2
+ Green #003F2C / #006C4B
+ Yellw #C8D400
+ Red #D81036
+-->
+
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 460.298 460.297" style="enable-background:new 0 0 460.298 460.297;"
+ xml:space="preserve">
+<g>
+ <g>
+ <path fill="#565656" d="M230.149,120.939L65.986,256.274c0,0.191-0.048,0.472-0.144,0.855c-0.094,0.38-0.144,0.656-0.144,0.852v137.041
+ c0,4.948,1.809,9.236,5.426,12.847c3.616,3.613,7.898,5.431,12.847,5.431h109.63V303.664h73.097v109.64h109.629
+ c4.948,0,9.236-1.814,12.847-5.435c3.617-3.607,5.432-7.898,5.432-12.847V257.981c0-0.76-0.104-1.334-0.288-1.707L230.149,120.939
+ z"/>
+ <path fill="#565656" d="M457.122,225.438L394.6,173.476V56.989c0-2.663-0.856-4.853-2.574-6.567c-1.704-1.712-3.894-2.568-6.563-2.568h-54.816
+ c-2.666,0-4.855,0.856-6.57,2.568c-1.711,1.714-2.566,3.905-2.566,6.567v55.673l-69.662-58.245
+ c-6.084-4.949-13.318-7.423-21.694-7.423c-8.375,0-15.608,2.474-21.698,7.423L3.172,225.438c-1.903,1.52-2.946,3.566-3.14,6.136
+ c-0.193,2.568,0.472,4.811,1.997,6.713l17.701,21.128c1.525,1.712,3.521,2.759,5.996,3.142c2.285,0.192,4.57-0.476,6.855-1.998
+ L230.149,95.817l197.57,164.741c1.526,1.328,3.521,1.991,5.996,1.991h0.858c2.471-0.376,4.463-1.43,5.996-3.138l17.703-21.125
+ c1.522-1.906,2.189-4.145,1.991-6.716C460.068,229.007,459.021,226.961,457.122,225.438z"/>
+
+<path fill="#246DA2" d="M 457.122 225.438 L 251.849 54.417 L 251.849 54.417 C 245.765 49.468 238.531 46.994 230.155 46.994 C 221.78 46.994 214.547 49.468 208.457 54.417 L 3.172 225.438 C 1.269 226.958 0.226 229.004 0.032 231.574 C -0.161 234.142 0.504 236.385 2.029 238.287 L 19.73 259.415 C 21.255 261.127 23.251 262.174 25.726 262.557 C 28.011 262.749 30.296 262.081 32.581 260.559 L 230.149 95.817 L 427.719 260.558 C 429.245 261.886 431.24 262.549 433.715 262.549 H 434.573 C 437.044 262.173 439.036 261.119 440.569 259.411 L 458.272 238.286 C 459.794 236.38 460.461 234.141 460.263 231.57 C 460.068 229.007 459.021 226.961 457.122 225.438 Z"/>
+ </g>
+</g>
+</svg>
diff --git a/sdnr/wt-odlux/odlux/framework/src/assets/icons/Menu.svg b/sdnr/wt-odlux/odlux/framework/src/assets/icons/Menu.svg
new file mode 100644
index 000000000..ea0312802
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/assets/icons/Menu.svg
@@ -0,0 +1,18 @@
+<!-- highstreet technologies GmbH colour scheme
+ Grey #565656
+ LBlue #36A9E1
+ DBlue #246DA2
+ Green #003F2C / #006C4B
+ Yellw #C8D400
+ Red #D81036
+-->
+
+<svg viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+
+<path fill="#36A9E1" d="M4,10h24c1.104,0,2-0.896,2-2s-0.896-2-2-2H4C2.896,6,2,6.896,2,8S2.896,10,4,10z"/>
+
+<path fill="#36A9E1" d="M28,14H4c-1.104,0-2,0.896-2,2 s0.896,2,2,2h24c1.104,0,2-0.896,2-2S29.104,14,28,14z"/>
+
+<path fill="#36A9E1" d="M28,22H4c-1.104,0-2,0.896-2,2s0.896,2,2,2h24c1.104,0,2-0.896,2-2 S29.104,22,28,22z"/>
+
+</svg> \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/assets/icons/Tools.svg b/sdnr/wt-odlux/odlux/framework/src/assets/icons/Tools.svg
new file mode 100644
index 000000000..1595cdc1c
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/assets/icons/Tools.svg
@@ -0,0 +1,35 @@
+<!-- highstreet technologies GmbH colour scheme
+ Grey #565656
+ LBlue #36A9E1
+ DBlue #246DA2
+ Green #003F2C / #006C4B
+ Yellw #C8D400
+ Red #D81036
+-->
+
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 265 265">
+
+<g transform="translate(0.000000,265.000000) scale(0.100000,-0.100000)">
+
+<path fill="#565656" d="M601 2515 c-35 -8 -66 -17 -69 -20 -3 -3 76 -87 175 -188 243 -244
+243 -246 59 -425 -106 -103 -137 -120 -194 -107 -19 4 -88 66 -207 185 -99 99
+-183 180 -186 180 -17 0 -34 -104 -33 -200 0 -82 6 -120 23 -170 61 -174 191
+-306 362 -367 62 -22 93 -27 187 -28 l113 -2 602 -600 c673 -671 633 -638 777
+-638 93 1 151 27 225 101 99 98 129 219 85 339 -21 57 -49 87 -626 665 l-603
+605 5 70 c18 217 -117 451 -316 549 -127 62 -250 78 -379 51z m1696 -1906 c70
+-34 113 -125 93 -199 -31 -116 -158 -168 -262 -107 -77 45 -107 133 -73 214
+21 50 42 72 90 94 53 24 99 24 152 -2z"/>
+
+<path fill="#565656" d="M2183 2496 c-23 -7 -56 -23 -75 -34 -18 -12 -172 -161 -343 -331
+l-310 -311 45 -45 c24 -25 49 -45 55 -45 5 0 152 142 325 315 264 264 319 315
+340 313 19 -2 26 -10 28 -32 3 -26 -26 -59 -315 -348 l-318 -318 38 -37 37
+-38 319 318 c287 287 321 318 347 315 24 -2 30 -8 32 -32 3 -26 -26 -59 -315
+-348 l-318 -318 48 -47 47 -48 320 320 c225 225 327 333 342 365 31 66 30 159
+-4 225 -66 127 -210 198 -325 161z"/>
+
+<path fill="#36A9E1" d="M800 875 l-175 -175 -85 0 -85 0 -135 -222 c-74 -121 -137 -225 -139
+-231 -4 -11 89 -107 104 -107 5 0 109 60 230 134 l220 133 3 84 3 84 177 178
+177 177 -60 60 -60 60 -175 -175z"/>
+
+</g>
+</svg>
diff --git a/sdnr/wt-odlux/odlux/framework/src/assets/icons/User.svg b/sdnr/wt-odlux/odlux/framework/src/assets/icons/User.svg
new file mode 100644
index 000000000..99618cf57
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/assets/icons/User.svg
@@ -0,0 +1,21 @@
+<!-- highstreet technologies GmbH colour scheme
+ Grey #565656
+ LBlue #36A9E1
+ DBlue #246DA2
+ Green #003F2C / #006C4B
+ Yellw #C8D400
+ Red #D81036
+-->
+
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 600 600">
+
+ <defs>
+ <clipPath id="circular-border">
+ <circle cx="300" cy="300" r="250" />
+ </clipPath>
+ </defs>
+
+ <circle fill="#36A9E1" cx="300" cy="300" r="280"/>
+ <circle fill="#ffffff" cx="300" cy="230" r="100" />
+ <circle fill="#ffffff" cx="300" cy="550" r="190" clip-path="url(#circular-border)" />
+</svg> \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/assets/icons/ht.Connect.png b/sdnr/wt-odlux/odlux/framework/src/assets/icons/ht.Connect.png
new file mode 100644
index 000000000..412390c79
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/assets/icons/ht.Connect.png
Binary files differ
diff --git a/sdnr/wt-odlux/odlux/framework/src/assets/icons/ht.Connect.svg b/sdnr/wt-odlux/odlux/framework/src/assets/icons/ht.Connect.svg
new file mode 100644
index 000000000..c1d74bc96
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/assets/icons/ht.Connect.svg
@@ -0,0 +1,81 @@
+<!-- highstreet technologies GmbH colour scheme
+ Grey #565656
+ LBlue #36A9E1
+ DBlue #246DA2
+ Green #003F2C / #006C4B
+ Yellw #C8D400
+ Red #D81036
+-->
+
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 795.000000 139.000000">
+
+<g transform="translate(0.000000,139.000000) scale(0.100000,-0.100000)">
+
+<path fill="#565656" d="M355 1163 c-38 -2 -75 -9 -82 -15 -10 -8 -13 -53 -13 -175 0 -175 -9
+-215 -50 -226 -17 -4 -20 -13 -20 -59 0 -48 3 -56 32 -80 l33 -27 3 -190 c4
+-226 -4 -213 135 -209 l92 3 0 70 0 70 -32 3 -33 3 0 148 c0 135 -2 151 -21
+175 l-20 26 20 26 c19 24 21 40 21 170 l0 143 33 3 32 3 0 70 0 70 -30 1 c-16
+0 -61 -1 -100 -3z"/>
+
+<path fill="#565656" d="M7473 1154 c-2 -6 -3 -38 -1 -70 3 -59 3 -59 36 -62 l32 -3 0 -137
+c0 -119 3 -141 20 -169 20 -33 20 -33 0 -65 -17 -29 -20 -51 -20 -175 l0 -142
+-32 -3 -33 -3 0 -70 0 -70 97 -3 c141 -4 138 -9 138 212 0 96 3 182 6 191 4 9
+17 21 30 26 21 8 24 15 24 67 0 53 -3 60 -30 80 l-30 23 0 172 c0 123 -4 178
+-12 189 -16 20 -217 31 -225 12z"/>
+
+<path fill="#246DA2" d="M645 1138 c-3 -7 -4 -215 -3 -463 l3 -450 90 0 90 0 0 195 c0 215 5
+237 65 280 71 50 146 45 178 -13 14 -27 17 -64 19 -247 l2 -215 88 0 88 0 0
+250 0 250 -27 55 c-33 67 -71 97 -138 110 -89 17 -181 -12 -240 -75 l-25 -27
+-5 179 -5 178 -88 3 c-64 2 -89 -1 -92 -10z"/>
+
+<path fill="#C8D400" d="M2499 1111 c-150 -48 -271 -193 -301 -361 -32 -184 85 -406 259 -492
+165 -81 408 -24 514 120 31 43 31 44 13 61 -10 10 -45 33 -76 50 l-57 32 -15
+-28 c-75 -145 -315 -142 -403 6 -135 226 18 510 249 462 69 -15 114 -42 139
+-86 12 -19 26 -35 31 -35 22 0 138 91 132 103 -89 153 -301 227 -485 168z"/>
+
+<path fill="#246DA2" d="M1467 1094 c-4 -4 -7 -51 -7 -105 l0 -98 -42 -3 -43 -3 0 -70 0 -70
+43 -3 42 -3 0 -197 c0 -185 1 -201 23 -242 26 -53 89 -90 152 -90 45 0 171 32
+187 48 7 7 4 30 -9 74 -16 52 -23 62 -39 60 -73 -11 -101 -12 -114 -3 -12 8
+-16 43 -18 180 l-3 170 68 3 68 3 0 70 0 70 -67 3 -67 3 -3 102 -3 102 -80 3
+c-45 1 -84 0 -88 -4z"/>
+
+<path fill="#C8D400" d="M7017 1094 c-4 -4 -7 -51 -7 -105 l0 -98 -37 -3 -38 -3 0 -70 0 -70
+36 -3 36 -3 7 -67 c3 -37 6 -130 6 -208 0 -130 2 -144 23 -175 27 -41 98 -79
+147 -79 36 0 120 19 172 39 l26 10 -15 58 c-19 74 -28 85 -59 72 -35 -13 -88
+-11 -102 3 -8 8 -12 63 -12 180 l0 167 68 3 67 3 0 70 0 70 -67 3 -67 3 -3
+102 -3 102 -85 3 c-47 1 -89 0 -93 -4z"/>
+
+<path fill="#C8D400" d="M3349 890 c-63 -11 -123 -43 -174 -95 -55 -54 -82 -106 -94 -181 -25
+-161 50 -304 198 -372 77 -36 211 -38 285 -4 75 34 129 84 167 154 32 59 34
+69 34 158 0 82 -4 103 -26 151 -46 98 -138 168 -249 189 -59 11 -75 11 -141 0z
+m168 -178 c44 -31 67 -86 67 -157 0 -113 -53 -176 -153 -183 -70 -5 -115 22
+-148 88 -53 103 -11 233 87 273 31 13 117 1 147 -21z"/>
+
+<path fill="#C8D400" d="M4254 890 c-61 -13 -106 -34 -147 -69 l-36 -32 -3 48 -3 48 -80 0
+-80 0 0 -330 0 -330 85 0 85 0 5 202 c5 184 7 204 26 229 45 60 111 88 171 72
+54 -14 58 -31 63 -278 l5 -225 85 0 85 0 0 266 0 266 -30 48 c-46 74 -132 106
+-231 85z"/>
+
+<path fill="#C8D400" d="M5040 890 c-63 -11 -89 -23 -142 -63 l-47 -36 -3 47 -3 47 -80 0 -80
+0 0 -330 0 -330 90 0 90 0 1 196 c1 158 4 201 18 226 37 73 151 111 205 69 28
+-22 35 -68 40 -276 l6 -215 85 0 85 0 0 245 c-1 264 -7 307 -51 360 -47 55
+-122 76 -214 60z"/>
+
+<path fill="#C8D400" d="M5709 890 c-99 -17 -200 -94 -250 -190 -21 -39 -24 -59 -24 -145 0
+-90 3 -105 28 -153 79 -149 241 -220 407 -178 79 21 145 61 188 118 39 51 34
+58 -54 77 -56 13 -56 13 -88 -16 -17 -16 -44 -35 -59 -41 -42 -18 -117 -15
+-157 7 -33 17 -79 81 -80 108 0 10 60 13 253 15 l252 3 -1 70 c-2 211 -197
+364 -415 325z m151 -150 c36 -19 80 -79 80 -110 0 -6 -56 -10 -160 -10 -149 0
+-160 1 -160 19 0 29 43 85 80 103 46 24 112 23 160 -2z"/>
+
+<path fill="#C8D400" d="M6483 890 c-186 -39 -303 -204 -273 -386 19 -114 95 -214 200 -263
+46 -22 69 -26 145 -26 76 0 99 4 145 26 65 30 125 80 146 120 17 33 15 35 -84
+64 l-62 18 -28 -25 c-40 -38 -65 -48 -120 -48 -91 0 -162 80 -162 182 0 84 36
+148 99 174 65 27 133 15 176 -32 l25 -27 80 23 c44 13 83 26 85 30 6 10 -42
+71 -82 104 -68 58 -194 86 -290 66z"/>
+
+<path fill="#36A9E1" d="M1934 407 c-2 -7 -4 -52 -2 -98 l3 -84 70 0 70 0 0 95 0 95 -68 3
+c-50 2 -69 -1 -73 -11z"/>
+
+</g>
+</svg>
diff --git a/sdnr/wt-odlux/odlux/framework/src/assets/images/defaultLogo.svg b/sdnr/wt-odlux/odlux/framework/src/assets/images/defaultLogo.svg
new file mode 100644
index 000000000..bd9ddf583
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/assets/images/defaultLogo.svg
@@ -0,0 +1,179 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ viewBox="0 0 350.6536 109.43979"
+ height="109.43979"
+ width="350.65359"
+ xml:space="preserve"
+ version="1.1"
+ id="svg2"
+ class="style-svg replaced-svg"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="htlogo_white.svg"><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1440"
+ inkscape:window-height="796"
+ id="namedview50"
+ showgrid="false"
+ inkscape:zoom="3.0651398"
+ inkscape:cx="123.67512"
+ inkscape:cy="44.346735"
+ inkscape:window-x="-8"
+ inkscape:window-y="1072"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="g12" /><metadata
+ id="metadata8"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
+ id="defs6"><linearGradient
+ id="linearGradient58"
+ spreadMethod="pad"
+ gradientTransform="matrix(700.116,-254.822,-254.822,-700.116,227.808,857.597)"
+ gradientUnits="userSpaceOnUse"
+ y2="0"
+ x2="1"
+ y1="0"
+ x1="0"><stop
+ id="stop60"
+ offset="0"
+ style="stop-opacity:1;stop-color:#45a0de" /><stop
+ id="stop62"
+ offset="0.509804"
+ style="stop-opacity:1;stop-color:#2da247" /><stop
+ id="stop64"
+ offset="1"
+ style="stop-opacity:1;stop-color:#f4f01f" /></linearGradient></defs><g
+ transform="matrix(1.25,0,0,-1.25,-25.640145,137.40654)"
+ id="g10"><g
+ transform="scale(0.1,0.1)"
+ id="g12"><path
+ id="path14"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ d="m 1089.71,559.016 c 0,-19.914 9.13,-38.637 47.77,-38.637 l 11.28,0 c 44.64,0 47.04,19.441 47.04,43.922 l 0,13.437 -16.8,0 0,-9.121 c 0,-25.679 -4.32,-31.437 -31.68,-31.437 l -6.48,0 c -22.57,0 -30.49,6.961 -30.49,29.758 l 0,60.96 81.85,0 0,16.801 -81.85,0 0,29.039 -20.64,0 0,-29.039 -26.88,0 0,-16.801 26.88,0 0,-68.882" /><path
+ id="path16"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ d="m 1215.44,570.543 c 0,-45.125 30.72,-50.164 57.6,-50.164 l 37.44,0 c 38.39,0 61.68,8.398 61.68,36.48 l 0,2.403 -20.64,0 c -1.2,-18.719 -11.76,-22.082 -37.68,-22.082 l -28.32,0 c -36.01,0 -49.44,7.199 -49.44,35.039 l 0,7.683 136.08,0 0,13.676 c 0,43.68 -26.64,53.043 -63.6,53.043 l -33.12,0 c -29.28,0 -60,-5.047 -60,-49.441 l 0,-26.637 z m 136.08,26.156 -115.44,0 c 1.92,27.84 9.6,33.121 42.24,33.121 l 30.24,0 c 33.6,0 42.96,-12.48 42.96,-24.965 l 0,-8.156" /><path
+ id="path18"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ d="m 1399.01,569.82 c 0,-44.402 32.88,-49.441 51.36,-49.441 l 46.56,0 c 38.4,0 53.76,12.004 53.76,42.965 l 0,3.594 -19.2,0 0,-4.079 c 0,-19.921 -16.08,-25.679 -35.28,-25.679 l -34.8,0 c -24.96,0 -41.76,6.242 -41.76,33.601 l 0,25.68 c 0,30.477 18.24,33.359 37.68,33.359 l 37.92,0 c 20.64,0 34.08,-4.8 34.08,-25.203 l 0,-2.879 19.2,0 0,3.364 c 0,25.918 -15.12,41.519 -53.52,41.519 l -34.56,0 c -30.24,0 -61.44,-5.047 -61.44,-49.441 l 0,-27.36" /><path
+ id="path20"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ d="m 1582.36,522.301 20.65,0 0,69.597 c 0,28.805 11.03,37.922 59.28,37.922 l 5.27,0 c 40.08,0 45.12,-11.761 45.12,-29.757 l 0,-77.762 20.64,0 0,82.801 c 0,22.078 -12.24,41.519 -50.88,41.519 l -29.52,0 c -25.68,0 -41.76,-4.078 -49.44,-23.043 l -0.47,0 0,72.481 -20.65,0 0,-173.758" /><path
+ id="path22"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ d="m 1772.14,522.301 20.65,0 0,69.597 c 0,28.805 11.03,37.922 59.28,37.922 l 5.28,0 c 40.08,0 45.12,-11.761 45.12,-29.757 l 0,-77.762 20.64,0 0,82.801 c 0,22.078 -12.25,41.519 -50.88,41.519 l -30.97,0 c -25.67,0 -41.76,-4.078 -49.43,-23.043 l -0.49,0 0,21.121 -19.2,0 0,-122.398" /><path
+ id="path24"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ d="m 2102.09,571.258 c 0,-28.559 -16.79,-34.078 -40.8,-34.078 l -42,0 c -23.99,0 -40.8,5.519 -40.8,34.078 l 0,24.484 c 0,28.559 16.81,34.078 40.8,34.078 l 42,0 c 24.01,0 40.8,-5.519 40.8,-34.078 l 0,-24.484 z m -144.25,-1.195 c 0,-40.321 28.57,-49.684 67.69,-49.684 l 29.52,0 c 39.12,0 67.69,9.363 67.69,49.684 l 0,26.875 c 0,40.32 -28.57,49.683 -67.69,49.683 l -29.52,0 c -39.12,0 -67.69,-9.363 -67.69,-49.683 l 0,-26.875" /><path
+ id="path26"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ d="m 2160.42,522.301 20.6484,0 0,173.758 -20.6484,0 0,-173.758 z" /><path
+ id="path28"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ d="m 2362.98,571.258 c 0,-28.559 -16.8,-34.078 -40.8,-34.078 l -41.99,0 c -24.01,0 -40.8,5.519 -40.8,34.078 l 0,24.484 c 0,28.559 16.79,34.078 40.8,34.078 l 41.99,0 c 24,0 40.8,-5.519 40.8,-34.078 l 0,-24.484 z m -144.24,-1.195 c 0,-40.321 28.56,-49.684 67.68,-49.684 l 29.52,0 c 39.12,0 67.69,9.363 67.69,49.684 l 0,26.875 c 0,40.32 -28.57,49.683 -67.69,49.683 l -29.52,0 c -39.12,0 -67.68,-9.363 -67.68,-49.683 l 0,-26.875" /><path
+ id="path30"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ d="m 2569.4,644.699 -19.21,0 0,-21.84 -0.48,0 c -6.47,19.918 -22.08,23.762 -37.68,23.762 l -43.92,0 c -23.04,0 -55.2,-5.047 -55.2,-49.441 l 0,-20.399 c 0,-31.445 17.04,-48.965 51.36,-48.965 l 42.48,0 c 28.8,0 37.2,7.442 41.52,18.965 l 0.47,0 0,-18.242 c 0,-33.121 -16.31,-33.598 -57.12,-33.598 l -20.14,0 c -19.21,0 -35.53,4.559 -35.53,20.395 l 0,3.363 -19.2,0 0,-4.082 c 0,-15.601 10.08,-36.476 48,-36.476 l 46.56,0 c 25.68,0 58.09,7.441 58.09,44.402 l 0,122.156 z M 2548.74,577.5 c 0,-21.117 -10.79,-32.883 -40.31,-32.883 l -39.84,0 c -25.2,0 -35.28,8.645 -35.04,36.723 l 0,22.558 c 0.24,20.157 15.36,25.922 36.48,25.922 l 42,0 c 29.04,0 37.2,-14.64 36.71,-37.437 l 0,-14.883" /><path
+ id="path32"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ d="m 2611.1,673.5 20.64,0 0,22.559 -20.64,0 0,-22.559 z m 0,-151.199 20.64,0 0,122.398 -20.64,0 0,-122.398" /><path
+ id="path34"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ d="m 2667.98,570.543 c 0,-45.125 30.71,-50.164 57.6,-50.164 l 37.44,0 c 38.39,0 61.68,8.398 61.68,36.48 l 0,2.403 -20.64,0 c -1.21,-18.719 -11.76,-22.082 -37.69,-22.082 l -28.31,0 c -36.01,0 -49.44,7.199 -49.44,35.039 l 0,7.683 136.08,0 0,13.676 c 0,43.68 -26.64,53.043 -63.6,53.043 l -33.12,0 c -29.29,0 -60,-5.047 -60,-49.441 l 0,-26.637 z m 136.08,26.156 -115.44,0 c 1.91,27.84 9.6,33.121 42.24,33.121 l 30.24,0 c 33.6,0 42.96,-12.48 42.96,-24.965 l 0,-8.156" /><path
+ id="path36"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ d="m 2852.5,557.098 c 0,-36.719 28.57,-36.719 59.05,-36.719 l 37.92,0 c 25.2,0 56.88,1.195 56.88,35.519 l 0,0.485 c 0,33.117 -21.11,36.238 -52.08,36.238 l -54.48,0 c -21.59,0 -24.96,5.762 -24.96,15.84 l 0,4.559 c 0,13.918 6.48,16.8 35.99,16.8 l 36.01,0 c 22.56,0 35.04,-0.48 35.04,-19.203 l 0,-1.195 19.2,0 0,6.476 c 0,17.286 -6.48,30.723 -53.04,30.723 l -31.68,0 c -50.88,0 -62.16,-11.039 -62.16,-30.957 l 0,-7.203 c 0,-23.281 10.8,-32.641 39.85,-32.641 l 63.59,0 c 24.72,0 28.08,-6.48 28.08,-18.238 l 0,-0.961 c 0,-18.238 -9.84,-19.441 -40.8,-19.441 l -44.17,0 c -21.11,0 -29.03,6.242 -29.03,21.359 l 0,6 -19.21,0 0,-7.441" /><path
+ id="path38"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ d="m 1078.67,762.301 20.65,0 0,69.597 c 0,28.805 11.03,37.922 59.28,37.922 l 5.28,0 c 40.08,0 45.12,-11.761 45.12,-29.757 l 0,-77.762 20.64,0 0,82.801 c 0,22.078 -12.24,41.519 -50.88,41.519 l -29.52,0 c -25.68,0 -41.76,-4.078 -49.44,-23.043 l -0.48,0 0,72.481 -20.65,0 0,-173.758" /><path
+ id="path40"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ d="m 1271.34,913.5 20.64,0 0,22.559 -20.64,0 0,-22.559 z m 0,-151.199 20.64,0 0,122.398 -20.64,0 0,-122.398" /><path
+ id="path42"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ d="m 1484.7,884.699 -19.19,0 0,-21.84 -0.49,0 c -6.48,19.918 -22.08,23.762 -37.68,23.762 l -43.92,0 c -23.04,0 -55.2,-5.047 -55.2,-49.441 l 0,-20.399 c 0,-31.445 17.04,-48.965 51.36,-48.965 l 42.48,0 c 28.8,0 37.2,7.442 41.52,18.965 l 0.48,0 0,-18.242 c 0,-33.121 -16.32,-33.598 -57.12,-33.598 l -20.16,0 c -19.2,0 -35.52,4.559 -35.52,20.395 l 0,3.363 -19.2,0 0,-4.082 c 0,-15.601 10.08,-36.476 48,-36.476 l 46.56,0 c 25.68,0 58.08,7.441 58.08,44.402 l 0,122.156 z M 1464.06,817.5 c 0,-21.117 -10.8,-32.883 -40.32,-32.883 l -39.84,0 c -25.2,0 -35.27,8.645 -35.04,36.723 l 0,22.558 c 0.24,20.157 15.36,25.922 36.48,25.922 l 42,0 c 29.04,0 37.2,-14.64 36.72,-37.437 l 0,-14.883" /><path
+ id="path44"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ d="m 1523.52,762.301 20.65,0 0,69.597 c 0,28.805 11.04,37.922 59.28,37.922 l 5.28,0 c 40.08,0 45.11,-11.761 45.11,-29.757 l 0,-77.762 20.65,0 0,82.801 c 0,22.078 -12.24,41.519 -50.88,41.519 l -29.52,0 c -25.68,0 -41.76,-4.078 -49.44,-23.043 l -0.48,0 0,72.481 -20.65,0 0,-173.758" /><path
+ id="path46"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ d="m 1708.75,797.098 c 0,-36.719 28.57,-36.719 59.04,-36.719 l 37.93,0 c 25.19,0 56.87,1.195 56.87,35.519 l 0,0.485 c 0,33.117 -21.11,36.238 -52.08,36.238 l -54.47,0 c -21.61,0 -24.96,5.762 -24.96,15.84 l 0,4.559 c 0,13.918 6.47,16.8 35.99,16.8 l 36.01,0 c 22.56,0 35.04,-0.48 35.04,-19.203 l 0,-1.195 19.2,0 0,6.476 c 0,17.286 -6.49,30.723 -53.04,30.723 l -31.69,0 c -50.88,0 -62.16,-11.039 -62.16,-30.957 l 0,-7.203 c 0,-23.281 10.8,-32.641 39.84,-32.641 l 63.6,0 c 24.72,0 28.09,-6.48 28.09,-18.238 l 0,-0.961 c 0,-18.238 -9.85,-19.441 -40.8,-19.441 l -44.17,0 c -21.12,0 -29.04,6.242 -29.04,21.359 l 0,6 -19.2,0 0,-7.441" /><path
+ id="path48"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ d="m 1907.92,799.016 c 0,-19.914 9.12,-38.637 47.77,-38.637 l 11.28,0 c 44.64,0 47.04,19.441 47.04,43.922 l 0,13.437 -16.81,0 0,-9.121 c 0,-25.679 -4.32,-31.437 -31.67,-31.437 l -6.48,0 c -22.56,0 -30.48,6.961 -30.48,29.758 l 0,60.96 81.84,0 0,16.801 -81.84,0 0,29.039 -20.65,0 0,-29.039 -26.87,0 0,-16.801 26.87,0 0,-68.882" /><path
+ id="path50"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ d="m 2040.85,762.301 20.64,0 0,77.762 c 0,15.835 10.55,29.757 32.4,29.757 l 12.71,0 c 36.72,0 42.97,-4.8 42.97,-35.043 l 0,-5.277 19.19,0 0,14.641 c 0,30 -12.71,42.48 -51.35,42.48 l -19.45,0 c -14.16,0 -29.75,-3.844 -37.43,-19.683 l -0.49,0 0,17.761 -19.19,0 0,-122.398" /><path
+ id="path52"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ d="m 2187.69,810.543 c 0,-45.125 30.72,-50.164 57.6,-50.164 l 37.44,0 c 38.4,0 61.68,8.398 61.68,36.48 l 0,2.403 -20.65,0 C 2322.57,780.543 2312,777.18 2286.09,777.18 l -28.32,0 c -36,0 -49.45,7.199 -49.45,35.039 l 0,7.683 136.09,0 0,13.676 c 0,43.68 -26.64,53.043 -63.61,53.043 l -33.11,0 c -29.28,0 -60,-5.047 -60,-49.441 l 0,-26.637 z m 136.07,26.156 -115.44,0 c 1.93,27.84 9.6,33.121 42.25,33.121 l 30.23,0 c 33.61,0 42.96,-12.48 42.96,-24.965 l 0,-8.156" /><path
+ id="path54"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ d="m 2371.26,810.543 c 0,-45.125 30.73,-50.164 57.6,-50.164 l 37.44,0 c 38.4,0 61.68,8.398 61.68,36.48 l 0,2.403 -20.64,0 c -1.19,-18.719 -11.76,-22.082 -37.68,-22.082 l -28.32,0 c -36.01,0 -49.44,7.199 -49.44,35.039 l 0,7.683 136.08,0 0,13.676 c 0,43.68 -26.64,53.043 -63.59,53.043 l -33.13,0 c -29.27,0 -60,-5.047 -60,-49.441 l 0,-26.637 z m 136.08,26.156 -115.44,0 c 1.92,27.84 9.6,33.121 42.24,33.121 l 30.25,0 c 33.59,0 42.95,-12.48 42.95,-24.965 l 0,-8.156" /><path
+ id="path56"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ d="m 2571.4,799.016 c 0,-19.914 9.11,-38.637 47.75,-38.637 l 11.28,0 c 44.64,0 47.04,19.441 47.04,43.922 l 0,13.437 -16.8,0 0,-9.121 c 0,-25.679 -4.31,-31.437 -31.67,-31.437 l -6.49,0 c -22.56,0 -30.48,6.961 -30.48,29.758 l 0,60.96 81.84,0 0,16.801 -81.84,0 0,29.039 -20.63,0 0,-29.039 -26.89,0 0,-16.801 26.89,0 0,-68.882" /><path
+ id="path66"
+ style="fill:url(#linearGradient58);fill-opacity:1;fill-rule:evenodd;stroke:none"
+ d="m 414.316,403.621 c -16.695,10.117 -53.621,35.262 -67.011,51.457 -12.836,8.742 -24.672,17.988 -35.45,27.621 -9.265,7.731 -17.839,16.301 -25.644,25.723 -1.172,1.41 -2.32,2.836 -3.441,4.273 -9.063,10.887 -16.875,22.106 -23.372,33.524 -7.628,13.414 -12.269,22.301 -17.019,40.851 -3.981,19.496 -5.234,32.43 -5.082,47.832 -28.332,58.625 -36.563,123.481 -18.883,186.891 13.09,46.949 38.906,86.59 73.777,118.02 7.528,17.125 16.887,33.05 28.067,47.488 10.691,16.629 20.637,30.769 49.383,49.629 27.644,18.15 77.902,31.47 117.879,30.93 18.476,13.53 40.089,20.71 64.839,24.74 47.262,5.39 79.168,2.22 116.493,-6.39 18.808,-4.33 45.027,-17.46 45.027,-17.46 -54.891,19.82 -87.711,22.51 -144.391,17.57 25.133,-8.53 54.653,-29.87 77.192,-58.13 44.23,28.77 86.351,35.13 116.25,19.84 l -4.11,-0.96 c -25.644,13.11 -58.992,5.79 -97.121,-20.08 91.344,7.4 169.336,-18.14 219.184,-78.318 l 0.125,-2.223 c -45.715,55.191 -120.848,79.461 -205.059,73.911 84.348,-25.176 153.828,-74.512 200.754,-135.11 63.703,-70.281 91.414,-159.559 66.129,-250.23 -15.387,-55.18 -48.344,-101.879 -92.93,-137.551 -2.129,-4.223 -14.382,-26.391 -21.718,-33.938 -13.5,-16.859 -30.008,-31.453 -49.657,-43.25 -26.961,-16.179 -56.652,-25.742 -87.773,-29.222 -3.781,-0.762 -7.613,-1.473 -11.484,-2.129 -11.52,-8.075 -25.625,-14.414 -41.614,-18.914 -6.265,-1.801 -12.777,-3.301 -19.418,-4.543 -36.254,-6.082 -81.039,-4.164 -124.425,7.011 -28.528,7.356 -55.516,18.207 -79.497,31.137 z m 337.618,279.375 -0.184,6.477 c 33.586,2.617 63.285,10.574 94.199,21.816 0,0 -50.402,-10.492 -94.547,-12.828 -0.718,16.012 -1.964,32.266 -3.761,48.687 l -0.34,3.075 c 13.105,37.785 22.176,70.148 22.176,70.148 -6.387,-20.391 -14.071,-41.488 -23.02,-63.016 -5.875,47.676 -16.012,92.579 -29.309,133.036 30.336,-10.758 58.536,-22.903 84.047,-36.328 53.414,-88.161 79.442,-183.672 73.118,-265.227 -4.391,-29.281 -13.012,-56.602 -25.758,-80.918 l -0.825,-0.684 c 0.918,9.688 0.629,19.559 -0.792,29.524 -0.165,-9.938 -1.122,-20.363 -2.911,-31.317 l -0.238,-1.398 c -36.898,-29.387 -82.035,-51.129 -131.93,-63.566 0.157,1.562 9.618,28.707 13.27,43.371 3.305,9.632 6.336,19.73 9.074,30.238 5.145,3.516 10.246,7.141 15.305,10.863 0,0 -5.188,-3.039 -14.551,-7.949 12.02,47.359 18.184,102.902 17.055,162.344 53.453,-42.227 87.926,-93.524 94.926,-142.586 1.113,67.512 -34.336,112.504 -95.004,146.238 z M 333.047,859.52 c -10.301,17.363 -17.945,33.175 -20.547,50.64 10.129,5.113 20.969,9.75 32.434,13.887 22.472,6.043 47.168,10.066 73.519,12.086 l -5.664,-4.117 C 382.016,909.348 355.977,886.105 333.047,859.52 Z m -20.945,53.605 c -1.668,13.578 -0.848,27.012 2.687,40.047 13.629,11.637 28.422,22.25 44.195,31.73 28.508,14.231 57.403,23.548 90.801,30.188 -8.578,-16.223 -19.293,-41.961 -28.226,-74.18 -29.555,-3.914 -52.071,-8.015 -76.625,-16.863 -11.555,-3.106 -22.52,-6.746 -32.832,-10.922 z m 348.73,95.825 c 18.594,-27.942 37.102,-64.559 50.938,-103.044 30.394,-11.972 58.132,-26.027 82.953,-41.59 l -3.489,5.422 c -35.339,54.348 -75.664,98.797 -120.496,132.012 50.84,-32.992 96.559,-79.105 134.168,-136.93 2.684,-4.117 5.285,-8.254 7.809,-12.402 10.969,-7.645 21.273,-15.586 30.898,-23.777 52.621,-35.407 88.274,-77.661 99.668,-125.266 -0.133,63.387 -24.105,127.047 -66.343,181.59 -51.118,56.394 -127.426,101.137 -216.106,123.985 z m 50.363,-345.43 c -23.25,-42.028 -48.918,-80.93 -75.531,-115.211 24.883,-14.922 45.047,-32.543 58.422,-51.286 15.269,6.786 27.965,13.024 37.336,17.86 6.832,43.519 8.812,100.844 4.648,173.199 -4.679,-0.559 -8.5,-0.398 -13.082,-0.754 -3.621,-7.969 -8.105,-17.148 -11.793,-23.808 z m -307.539,95.351 c -24.816,-2.672 -43.742,-6.769 -64.582,-13.402 17.864,8.511 37.91,15.195 59.688,19.832 -28.746,19.629 -51.621,41.957 -67.949,65.793 -18.45,-22.781 -34.235,-46.406 -47.02,-70.227 2.09,-12.176 4.801,-24.461 8.125,-36.797 13.629,8.61 29.48,15.77 47.156,21.399 -17.113,-8.156 -32.226,-17.985 -44.941,-29.293 4.719,-16.25 14.672,-42.125 23.359,-61.703 14.754,-12.203 39.66,-30.582 57.074,-41.969 9.215,33.777 17.274,58.293 33.485,94.824 -2.485,21.758 -4.395,51.543 -4.395,51.543 z M 613.887,373.906 c 13.918,2.422 26.574,6.153 37.629,11.035 l 0.75,0.332 c 2.812,1.266 5.519,2.602 8.113,4.016 -43.27,0.524 -88.383,11.902 -132.074,32.734 14.156,-16.507 28.933,-29.168 43.867,-37.355 26.258,-2.254 51.816,-2.172 76.109,-0.031 -21.16,-3.254 -43.258,-5.032 -65.879,-4.867 10.524,-4.297 21.075,-6.325 31.485,-5.864 z m 79.144,26.282 c 28.856,3.609 56.262,12.652 81.074,27.55 25.086,15.063 45.735,35.051 61.825,58.731 -36.25,-25.774 -79.078,-44.801 -125.723,-55.922 l -0.363,-1.356 c -3.047,-10.918 -8.809,-20.609 -16.813,-29.003 z m -147.797,24.066 c 43.317,-18.582 87.555,-27.516 129.371,-25.621 10.231,8.133 17.657,17.84 21.668,28.851 -47.679,-9.605 -98.953,-11.078 -151.039,-3.23 z m -46.187,634.796 c -9.406,-8.19 -17.121,-16.69 -23.402,-30.59 44.066,9.86 84.668,12.07 132.925,5.93 -37.882,15.93 -72.554,24.28 -109.523,24.66 z m 131.582,-28.07 c -28.652,33.59 -65.481,54.73 -95.899,47.25 -10.156,-3.17 -16.96,-7.05 -25.121,-11.99 38.176,-2.87 73.696,-15.34 121.02,-35.26 z M 571.035,708.031 c 13.711,-3.531 26.285,-7.843 38.695,-10.492 -24.523,1.531 -66.074,7.684 -86.843,12.742 -38.117,9.282 -54.282,16.27 -84.098,31.43 -5.652,-11.711 -10.98,-23.336 -15.98,-34.844 4.886,-40.812 12.664,-79.394 22.64,-114.832 33.672,2.77 70.922,-0.226 107.047,-9.531 27.527,-7.094 53.047,-17.301 75.242,-29.617 26.407,33.894 52.102,72.609 75.555,115.004 3.164,5.714 7.234,12.988 10.238,18.691 -33.445,1.5 -56.398,3.75 -103.801,10.957 36.25,-2.266 109.875,1.176 109.875,1.176 -37.574,16.551 -82.113,30.308 -131.773,43.094 -48.844,12.582 -96.305,18.644 -139.715,18.726 l -1.582,-3.601 c 36.336,-20.719 77.582,-36.817 124.5,-48.903 z m -254.148,-73.84 c -19.922,17.575 -32.805,29.309 -49.817,49.231 -7.968,-12.262 -11.718,-21.731 -14.937,-36.039 -1.153,-5.129 -2.219,-10.285 -3.129,-15.457 18.773,-36.582 45.68,-70.598 79.277,-99.985 5.547,13.598 16.114,25.075 30.332,34.286 -5.121,7.039 -10.097,14.265 -14.906,21.664 -13.184,20.269 -17.164,25.371 -26.82,46.3 z m 108.781,-52.16 10.262,-5.273 -2.106,6.859 c -1.621,-0.234 -6.562,-1.316 -8.156,-1.586 z M 274.469,695.734 c 6.469,-6.199 20.172,-21.121 27.304,-27.211 -3.793,9.981 -11.925,30.747 -14.937,40.735 -3.656,-3.699 -9.203,-9.547 -12.367,-13.524 z m -48.09,118.813 c -4.387,-18.231 -4.055,-36.277 0.418,-54.02 -2.961,5.801 -5.379,11.559 -7.195,17.254 -4.196,-42.461 3.519,-87.199 20.277,-127.246 0.488,2.547 1.344,10.11 2.047,14.195 2.453,15.508 4.5,26.016 8.16,42.129 -11.617,16.539 -18.949,36.438 -23.289,53.668 6.652,-13.035 16.047,-26.269 27.484,-39.457 5.008,15.985 11.407,32 19.157,47.875 -6.043,41.114 -5.258,81.02 2.285,117.629 -25.407,-20 -42.684,-44.324 -49.344,-72.027 z m 709.922,-170 c 7.422,59.746 -26.321,127.641 -92.688,184.094 -8.418,5.664 -17.269,11.152 -26.527,16.461 50.605,-86.379 68.984,-178.438 57.273,-256.524 -1.992,-25.672 -7.187,-49.973 -15.75,-72.219 36.059,32.825 62.625,73.719 75.758,120.797 0.684,2.457 1.328,4.922 1.934,7.391 z M 288.707,901.691 c 4.258,2.336 8.668,4.547 13.223,6.653 -0.895,10.953 -0.469,21.957 1.328,32.918 -5.895,-12.508 -10.746,-25.746 -14.551,-39.571 z m -40.098,-30.046 c 8.368,8.699 18.09,16.539 29.02,23.503 2.473,10.395 5.512,20.497 9.109,30.262 -14.898,-16.504 -27.73,-34.504 -38.129,-53.765 z m -2.246,-260.403 c 1.266,-16.219 5.2,-31.91 10.817,-46.492 12.215,-27.102 33.175,-50.285 59.043,-73.496 4.597,-3.379 9.289,-6.559 14.054,-9.535 -5.996,13.586 -7.953,27.398 -4.929,40.883 -32.395,27.742 -58.981,59.703 -78.493,94.207 -0.195,-1.852 -0.359,-3.711 -0.492,-5.567 z m 30.75,136.61 c -5.777,-11.817 -11.312,-25.586 -15.55,-37.36 2.093,-2.242 3.324,-4.086 5.527,-6.324 4.711,4.633 11.34,10.539 16.988,14.609 -2.691,9.723 -5.012,19.418 -6.965,29.075 z m 137.813,166.109 c -30.09,-22.957 -50.508,-39.84 -73.742,-66.703 17.101,-24.082 32.175,-42.192 61.769,-63.039 0.129,59.617 5.953,98.562 11.973,129.742 z m 63.347,42.805 c -5.769,-3.34 -11.503,-6.809 -17.207,-10.414 8.411,0.515 16.961,0.828 25.625,0.937 -3.015,3.121 -5.82,6.285 -8.418,9.477 z m 5.891,16.363 c 41.988,22.187 80.035,37.611 122.715,46.381 -48.469,6.85 -88.973,8.11 -133.481,0.79 -2.347,-15.62 1.137,-32.628 10.766,-47.171 z m -47.473,-25.246 c 11.52,7.613 21.438,13.832 33.282,20.383 -7.852,12.394 -11.328,23.336 -12.668,35.564 -7.832,-17.58 -14.629,-33.74 -20.614,-55.947 z m -77.476,36.734 c 29.711,17.853 59.988,30.823 95.445,39.893 0,0 14.356,22.66 25.043,34.4 -40.375,-4.82 -65.492,-11.69 -98.254,-28.31 -22.383,-11.36 -37.285,-24.19 -50.422,-43.116 -6.414,-9.73 -10.398,-19.398 -13.824,-28.066 13.11,9.375 27.156,17.781 42.012,25.199 z m -78.559,-201.64 c 11.453,21.168 25.301,41.996 41.426,62.089 -10.82,19.153 -17.516,39.094 -19.762,59.239 -5.382,-3.043 -10.527,-6.235 -15.429,-9.567 -8.5,-34.457 -10.645,-72.351 -6.235,-111.761 z M 688.488,485.801 c -48.293,-26.285 -98.433,-43.637 -147,-51.828 54.532,-8.883 108.055,-7.297 157.36,3.226 2.632,15.403 -1.121,32.106 -10.36,48.602 z m 45.766,228.554 c -2.285,-5.847 -4.633,-11.621 -7.024,-17.242 l 1.067,-0.707 7.168,0.512 c -0.367,5.715 -0.766,11.523 -1.211,17.437 z M 725.172,483.59 c 2.152,8.66 4.066,17.965 5.711,27.953 -10.805,-7.273 -21.766,-14.078 -32.84,-20.406 8.266,-13.121 13.172,-26.696 14.043,-40.196 4.742,10.161 9.109,21.075 13.086,32.649 z M 512.363,445.281 c 37.364,25.75 77.864,62.559 111.164,102.242 -20.925,12.305 -45.597,22.641 -73.281,29.778 -36.816,9.48 -72.16,11.66 -102.891,8.097 0.922,-3.14 4.317,-13.179 5.274,-16.273 39.855,-18.266 82.976,-33.98 82.976,-33.98 -27.621,6.031 -53.914,12.203 -79.226,21.687 15.574,-47.457 55.98,-111.648 55.984,-111.551 z m -67.359,-31.867 c 13.047,3.754 26.023,8.07 39.937,15.262 -40.523,-0.938 -67.214,2.875 -101.777,14.558 22.129,-12.675 35.953,-20.589 61.84,-29.82 z m -30.75,0.816 c 4.848,-2.921 11.637,-2.921 15.141,-2.453 -12.45,4.793 -18.77,7.582 -32.567,14.325 7.016,-5.438 9.473,-7.309 17.426,-11.872 z m -45.274,75.879 c 0.313,-10.523 2.223,-20.601 4.047,-29.406 29.856,-11.246 60.684,-16.226 92.512,-17.066 -35.039,11.902 -67.43,27.664 -96.559,46.472 z m 4.266,56.989 c -2.637,-16.524 -4.094,-32 -4.328,-46.207 33.539,-22.34 71.766,-40.559 113.723,-53.27 -39.629,25.649 -76.993,59.184 -109.395,99.477 z m 2.574,14.547 -0.496,-2.629 c 32.727,-41.637 74.149,-80.024 118.262,-107.895 -19.68,31.887 -38.766,66.445 -53.27,112.07 -8.562,3.965 -22.011,9.454 -30.23,13.563 -17.523,-4.359 -22,-6.926 -34.266,-15.109 z m 6.129,29.183 c -1.758,-9.969 -1.23,-6.445 -2.926,-13.933 9.282,3.906 7.297,3.8 18.157,6.308 -8.414,4.363 -7.239,3.117 -15.231,7.625 z m 29.731,89.004 c -12.203,-29.008 -17.723,-48.094 -25.524,-74.707 15.016,-8.785 18.176,-11.496 30.379,-18.172 4.27,0.656 10.77,2.508 15.188,2.969 -8.993,33.039 -14.309,54.793 -20.043,89.91 z m 147.05,250.07 c -4.3,-5.285 -7.832,-9.629 -12.117,-15.191 37.313,-22.055 80.684,-32.52 80.684,-32.52 -8.387,1.184 -12.859,1.758 -27.449,4.938 -21.16,5.004 -43.539,10.957 -59.344,19.957 -25.684,-33.961 -51.887,-72.035 -75.234,-114.242 -3.782,-6.824 -7.454,-13.641 -11.028,-20.449 41.949,2.105 88,-2.434 135.844,-14.754 49.012,-12.629 93.297,-32.336 130.805,-56.379 4.316,8.605 8.441,17.176 12.382,25.699 -0.507,6.207 -1.062,12.519 -1.644,18.941 -5.008,54.649 -15.656,105.219 -30.172,149.618 -23.234,7.691 -47.613,14.589 -72.91,20.656 -24.172,5.789 -46.348,10.308 -69.817,13.726 z m 84.836,82.848 c -21.144,-15.355 -51.222,-43.402 -75.105,-70.973 21.824,-2.793 42.984,-6.07 65.398,-11.441 21.641,-5.188 42.313,-11.375 61.961,-18.434 -15.25,39.45 -34.152,75 -52.254,100.848 z m -90.449,-68.551 c 24.438,27.528 49.297,50.981 73.449,69.341 -46.043,-9 -90.203,-24.47 -136.121,-49.919 4.145,-4.953 8.61,-11.98 13.539,-16.676 17.067,-0.222 31.352,-0.894 49.133,-2.746 z m -18.551,-22.453 c 3.032,3.727 4.895,6.617 7.946,10.219 -9.293,1.176 -15.43,1.633 -24.543,2.426 5.972,-4.868 10.304,-8.231 16.597,-12.645 z M 436.363,773.141 c 4.457,8.707 9.321,17.078 14.285,25.707 22.571,39.211 49.93,80.941 77.497,115.609 -10.946,6.875 -20.879,14.125 -29.618,21.676 -17.609,1.097 -34.793,1.469 -51.425,1.097 -5.895,-3.98 -10.344,-7.226 -16.145,-11.488 -9.559,-44.469 -14.918,-94.359 -12.781,-151.203 l 3.16,-2.148 c 5.012,0.707 9.871,0.246 15.027,0.75 z M 419.77,736.914 c 1.832,3.973 3.699,7.957 5.601,11.945 -2.273,1.278 -4.519,2.563 -6.738,3.864 0.324,-5.235 0.703,-10.504 1.137,-15.809 z M 365.832,571.656 c 1.977,11.266 2.617,13.098 5.559,25.016 -17.149,10.098 -22.774,13.883 -37.493,24.617 7.684,-15.121 7.758,-13.68 17.375,-28.473 4.707,-7.234 9.563,-14.293 14.559,-21.16 z m -5.91,-64.59 c -0.125,13.739 0.82,28.489 2.793,44.063 -8.481,-8.254 -14.43,-17.691 -17.313,-28.047 -0.41,-1.473 -0.757,-2.949 -1.035,-4.445 5.051,-3.961 10.242,-7.817 15.555,-11.571 z m 4.719,-43.027 c -2.293,9.727 -3.739,20.383 -4.368,31.852 -5.75,3.929 -11.371,7.968 -16.851,12.129 0.098,-13.27 4.816,-27.082 13.492,-40.618 2.559,-1.168 5.137,-2.293 7.727,-3.363 z m 89.195,-61.324 c -4.52,-0.969 -8.941,-1.621 -13.266,-1.957 16.895,-7.879 35.582,-14.621 55.731,-19.813 29.906,-7.703 58.527,-10.898 84.375,-10.238 -6.406,2.5 -12.75,5.773 -19.008,9.762 -22.934,1.386 -46.266,4.855 -69.586,10.863 -13.176,3.395 -25.93,7.203 -38.246,11.383 z m 55.227,24.047 c -15.844,-10.192 -27.801,-15.782 -42.344,-20.543 9.496,-3.114 19.203,-5.957 29.117,-8.508 19.066,-4.914 38.074,-8.527 56.816,-10.949 -13.984,10.609 -31.91,25.652 -43.589,40 z M 631.309,542.75 c -29.774,-37.652 -60.614,-69.477 -90.477,-93.41 54.66,9.137 105.574,27.234 143.426,43.41 -11.699,17.816 -29.731,35.137 -52.949,50" /><path
+ id="path68"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 1095.13,368.133 21.29,0 62.34,-124.289 0.38,0 0,124.289 13.43,0 0,-138.867 -21.29,0 -62.34,124.289 -0.38,0 0,-124.289 -13.43,0 0,138.867" /><path
+ id="path70"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 1270.6,287.574 c 0,20.715 -0.38,29.918 -22.25,29.918 -21.86,0 -22.25,-9.203 -22.25,-29.918 l 44.5,0 z m -44.5,-12.277 0,-9.016 c 0,-21.289 3.64,-27.426 20.52,-27.426 19.76,0 23.98,0.387 23.98,19.567 l 12.27,0 0,-3.649 c 0,-19.558 -4.79,-27.039 -34.14,-27.039 -32.22,0 -36.06,10.739 -36.06,50.446 0,40.468 6.53,50.441 36.06,50.441 29.35,0 34.14,-11.894 34.14,-38.555 l 0,-14.769 -56.77,0" /><path
+ id="path72"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 1306.45,315.961 -10.17,0 0,11.125 10.17,0 0,22.637 12.27,0 0,-22.637 31.65,0 0,-11.125 -31.65,0 0,-65.406 c 0,-8.055 2.49,-11.7 10.55,-11.7 9.21,0 13.24,1.731 13.24,23.399 l 11.12,0 c 0,-31.258 -9.97,-34.52 -24.17,-34.52 -18.03,0 -23.01,5.563 -23.01,20.137 l 0,68.09" /><path
+ id="path74"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 1358.22,327.086 12.09,0 19.37,-85.352 0.38,0 18.41,85.352 17.65,0 18.41,-85.543 0.39,0 19.37,85.543 12.08,0 -23.21,-97.82 -17.83,0 -17.84,85.543 -0.39,0 -17.83,-85.543 -17.84,0 -23.21,97.82" /><path
+ id="path76"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 1522.18,238.855 c 19.57,0 22.63,2.879 22.63,39.325 0,36.437 -3.06,39.312 -22.63,39.312 -19.56,0 -22.63,-2.875 -22.63,-39.312 0,-36.446 3.07,-39.325 22.63,-39.325 z m 0,89.766 c 32.23,0 36.06,-11.508 36.06,-50.441 0,-40.477 -4.22,-50.446 -36.06,-50.446 -31.84,0 -36.06,9.969 -36.06,50.446 0,38.933 3.84,50.441 36.06,50.441" /><path
+ id="path78"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 1576.82,327.086 12.28,0 0,-11.891 0.38,0 c 4.6,9.399 10.36,13.426 22.64,13.426 15.53,0 22.63,-6.715 22.63,-26.277 l 0,-7.864 -11.12,0 c 0.19,18.028 -1.92,23.012 -15.93,23.012 -12.27,0.199 -18.6,-6.707 -18.6,-20.133 l 0,-68.093 -12.28,0 0,97.82" /><path
+ id="path80"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 1665.42,275.297 -5.37,0 0,-46.031 -12.27,0 0,138.867 12.27,0 0,-80.559 5.75,0 28.97,39.512 15.15,0 -34.33,-45.266 39.51,-52.554 -16.31,0 -33.37,46.031" /><path
+ id="path82"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 1787.76,259.57 c -0.38,-16.875 2.68,-20.715 20.91,-20.715 19.75,0 23.39,2.879 23.39,19.372 0,12.851 -1.33,13.812 -24.55,15.156 -24.55,1.344 -31.26,4.984 -31.26,26.66 0,20.523 3.84,28.578 32.99,28.578 28.59,0 34.14,-6.523 34.14,-27.047 l -12.27,0 c 0,14.965 -4.03,15.918 -22.44,15.918 -17.08,0 -20.14,-1.722 -20.14,-16.875 0,-13.812 1.73,-14.961 22.44,-16.109 28.96,-1.731 33.37,-4.031 33.37,-25.508 0,-23.02 -4.41,-31.266 -36.06,-31.266 -22.44,0 -32.8,2.684 -32.8,31.836 l 12.28,0" /><path
+ id="path84"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 1898.8,238.855 c 19.56,0 22.63,2.879 22.63,39.325 0,36.437 -3.07,39.312 -22.63,39.312 -19.57,0 -22.63,-2.875 -22.63,-39.312 0,-36.446 3.06,-39.325 22.63,-39.325 z m 0,89.766 c 32.22,0 36.06,-11.508 36.06,-50.441 0,-40.477 -4.22,-50.446 -36.06,-50.446 -31.84,0 -36.06,9.969 -36.06,50.446 0,38.933 3.84,50.441 36.06,50.441" /><path
+ id="path86"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 1956.7,229.266 12.2813,0 0,138.867 -12.2813,0 0,-138.867 z" /><path
+ id="path88"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 2061.04,229.266 -12.28,0 0,12.664 -0.39,0 c -6.13,-10.555 -12.85,-14.196 -27.04,-14.196 -22.64,0 -28.77,11.313 -28.77,30.301 l 0,69.051 12.28,0 0,-66.172 c 0,-11.316 2.49,-22.059 18.6,-22.059 14.96,0 25.32,5.758 25.32,26.469 l 0,61.762 12.28,0 0,-97.82" /><path
+ id="path90"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 2084.61,315.961 -10.16,0 0,11.125 10.16,0 0,22.637 12.28,0 0,-22.637 31.65,0 0,-11.125 -31.65,0 0,-65.406 c 0,-8.055 2.49,-11.7 10.54,-11.7 9.21,0 13.24,1.731 13.24,23.399 l 11.12,0 c 0,-31.258 -9.97,-34.52 -24.16,-34.52 -18.03,0 -23.02,5.563 -23.02,20.137 l 0,68.09" /><path
+ id="path92"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 2148.47,327.086 12.28,0 0,-97.82 -12.28,0 0,97.82 z m 0,41.047 12.28,0 0,-16.301 -12.28,0 0,16.301" /><path
+ id="path94"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 2218.67,238.855 c 19.56,0 22.62,2.879 22.62,39.325 0,36.437 -3.06,39.312 -22.62,39.312 -19.58,0 -22.64,-2.875 -22.64,-39.312 0,-36.446 3.06,-39.325 22.64,-39.325 z m 0,89.766 c 32.21,0 36.05,-11.508 36.05,-50.441 0,-40.477 -4.22,-50.446 -36.05,-50.446 -31.85,0 -36.07,9.969 -36.07,50.446 0,38.933 3.84,50.441 36.07,50.441" /><path
+ id="path96"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 2273.3,327.086 12.28,0 0,-12.656 0.38,0 c 6.14,10.547 12.85,14.191 27.05,14.191 22.63,0 28.77,-11.316 28.77,-30.305 l 0,-69.05 -12.28,0 0,66.172 c 0,11.32 -2.5,22.054 -18.6,22.054 -14.96,0 -25.32,-5.75 -25.32,-26.461 l 0,-61.765 -12.28,0 0,97.82" /><path
+ id="path98"
+ style="fill:#e2e3e4;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 2374.17,259.57 c -0.38,-16.875 2.69,-20.715 20.91,-20.715 19.76,0 23.4,2.879 23.4,19.372 0,12.851 -1.34,13.812 -24.55,15.156 -24.55,1.344 -31.26,4.984 -31.26,26.66 0,20.523 3.84,28.578 32.99,28.578 28.58,0 34.14,-6.523 34.14,-27.047 l -12.28,0 c 0,14.965 -4.02,15.918 -22.44,15.918 -17.07,0 -20.14,-1.722 -20.14,-16.875 0,-13.812 1.73,-14.961 22.45,-16.109 28.96,-1.731 33.36,-4.031 33.36,-25.508 0,-23.02 -4.4,-31.266 -36.05,-31.266 -22.44,0 -32.8,2.684 -32.8,31.836 l 12.27,0" /></g></g></svg> \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/assets/images/defaultLogo.svg.d.ts b/sdnr/wt-odlux/odlux/framework/src/assets/images/defaultLogo.svg.d.ts
new file mode 100644
index 000000000..60e4856dd
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/assets/images/defaultLogo.svg.d.ts
@@ -0,0 +1,19 @@
+/**
+ * ============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==========================================================================
+ */
+declare const path: string;
+export default path; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/assets/images/home.svg b/sdnr/wt-odlux/odlux/framework/src/assets/images/home.svg
new file mode 100644
index 000000000..080d0502e
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/assets/images/home.svg
@@ -0,0 +1,20 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="460.298px" height="460.297px" viewBox="0 0 460.298 460.297" style="enable-background:new 0 0 460.298 460.297;"
+ xml:space="preserve">
+<g>
+ <g>
+ <path fill="#565656" d="M230.149,120.939L65.986,256.274c0,0.191-0.048,0.472-0.144,0.855c-0.094,0.38-0.144,0.656-0.144,0.852v137.041
+ c0,4.948,1.809,9.236,5.426,12.847c3.616,3.613,7.898,5.431,12.847,5.431h109.63V303.664h73.097v109.64h109.629
+ c4.948,0,9.236-1.814,12.847-5.435c3.617-3.607,5.432-7.898,5.432-12.847V257.981c0-0.76-0.104-1.334-0.288-1.707L230.149,120.939
+ z"/>
+ <path fill="#565656" d="M457.122,225.438L394.6,173.476V56.989c0-2.663-0.856-4.853-2.574-6.567c-1.704-1.712-3.894-2.568-6.563-2.568h-54.816
+ c-2.666,0-4.855,0.856-6.57,2.568c-1.711,1.714-2.566,3.905-2.566,6.567v55.673l-69.662-58.245
+ c-6.084-4.949-13.318-7.423-21.694-7.423c-8.375,0-15.608,2.474-21.698,7.423L3.172,225.438c-1.903,1.52-2.946,3.566-3.14,6.136
+ c-0.193,2.568,0.472,4.811,1.997,6.713l17.701,21.128c1.525,1.712,3.521,2.759,5.996,3.142c2.285,0.192,4.57-0.476,6.855-1.998
+ L230.149,95.817l197.57,164.741c1.526,1.328,3.521,1.991,5.996,1.991h0.858c2.471-0.376,4.463-1.43,5.996-3.138l17.703-21.125
+ c1.522-1.906,2.189-4.145,1.991-6.716C460.068,229.007,459.021,226.961,457.122,225.438z"/>
+
+<path fill="#D81036" d="M 457.122 225.438 L 251.849 54.417 L 251.849 54.417 C 245.765 49.468 238.531 46.994 230.155 46.994 C 221.78 46.994 214.547 49.468 208.457 54.417 L 3.172 225.438 C 1.269 226.958 0.226 229.004 0.032 231.574 C -0.161 234.142 0.504 236.385 2.029 238.287 L 19.73 259.415 C 21.255 261.127 23.251 262.174 25.726 262.557 C 28.011 262.749 30.296 262.081 32.581 260.559 L 230.149 95.817 L 427.719 260.558 C 429.245 261.886 431.24 262.549 433.715 262.549 H 434.573 C 437.044 262.173 439.036 261.119 440.569 259.411 L 458.272 238.286 C 459.794 236.38 460.461 234.141 460.263 231.57 C 460.068 229.007 459.021 226.961 457.122 225.438 Z"/>
+ </g>
+</g>
+</svg>
diff --git a/sdnr/wt-odlux/odlux/framework/src/assets/images/home.svg.d.ts b/sdnr/wt-odlux/odlux/framework/src/assets/images/home.svg.d.ts
new file mode 100644
index 000000000..7098d79a2
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/assets/images/home.svg.d.ts
@@ -0,0 +1,20 @@
+/**
+ * ============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==========================================================================
+ */
+
+ declare const home: string;
+ export default home; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/assets/images/odluxLogo.gif b/sdnr/wt-odlux/odlux/framework/src/assets/images/odluxLogo.gif
new file mode 100644
index 000000000..ad188a8d3
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/assets/images/odluxLogo.gif
Binary files differ
diff --git a/sdnr/wt-odlux/odlux/framework/src/assets/images/odluxLogo.gif.d.ts b/sdnr/wt-odlux/odlux/framework/src/assets/images/odluxLogo.gif.d.ts
new file mode 100644
index 000000000..36131834c
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/assets/images/odluxLogo.gif.d.ts
@@ -0,0 +1,20 @@
+/**
+ * ============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==========================================================================
+ */
+
+declare const odluxLogo: string;
+export default odluxLogo; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/assets/images/onapLogo.gif b/sdnr/wt-odlux/odlux/framework/src/assets/images/onapLogo.gif
new file mode 100644
index 000000000..cd7eb8f3c
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/assets/images/onapLogo.gif
Binary files differ
diff --git a/sdnr/wt-odlux/odlux/framework/src/assets/images/onapLogo.gif.d.ts b/sdnr/wt-odlux/odlux/framework/src/assets/images/onapLogo.gif.d.ts
new file mode 100644
index 000000000..5bfb37a84
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/assets/images/onapLogo.gif.d.ts
@@ -0,0 +1,20 @@
+/**
+ * ============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==========================================================================
+ */
+
+ declare const onapLogo: string;
+ export default onapLogo; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/assets/version.json b/sdnr/wt-odlux/odlux/framework/src/assets/version.json
new file mode 100644
index 000000000..6d4eb0cc6
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/assets/version.json
@@ -0,0 +1,4 @@
+{
+ "version":"56.139cd6d(20/07/08)",
+ "build":"2020-07-16T06:06:24Z"
+} \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/common/event.ts b/sdnr/wt-odlux/odlux/framework/src/common/event.ts
new file mode 100644
index 000000000..913487911
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/common/event.ts
@@ -0,0 +1,81 @@
+/**
+ * ============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 an event.
+ * Events enable a class or object to notify other classes or objects when something of interest occurs.
+ * The class that sends (or invokes) the event is called the publisher and the classes that receive (or handle) the event are called subscribers.
+ *
+ * Objects can create an instances of an Events and offer that Events for other objects to attach to.
+ * Objects who want to be informed about an Event can attach a function (an event handler) to the event which is then called when the event is fired.
+ *
+ * @template TEventArg Type of the event argument. Use void if the event does not has an argument.
+ */
+export class Event<TEventArg> {
+
+ /**
+ * Creates a new instance of the Event class.
+ */
+ constructor() {
+ this.eventHandlers = new Array<(arg: TEventArg) => void>();
+ }
+
+ /**
+ * Adds an event handler to this event, so that when the event is fired the given event handler function is called.
+ *
+ * @param eventHandler The event handler function to add to this event.
+ * @throws {Error} Thrown if the given event handler function has already been added to this event.
+ */
+ public addHandler = (eventHandler: (arg: TEventArg) => void): void => {
+ if (this.eventHandlers.indexOf(eventHandler) > -1) {
+ throw new Error("The given event handler is already added to this event.");
+ }
+
+ this.eventHandlers.push(eventHandler);
+ }
+
+ /**
+ * Removes an event handler from this event, so that the given event handler function will not be called anymore when the event is fired.
+ *
+ * @param eventHandler: The event handler function to remove.
+ * @throws {Error} Thrown if the given event handler function has not been added to this event before.
+ */
+ public removeHandler = (eventHandler: (arg: TEventArg) => void): void => {
+ const index = this.eventHandlers.indexOf(eventHandler);
+ if (!(index > -1)) {
+ throw new Error("The given event handler has not been added to this event yet.");
+ }
+
+ this.eventHandlers.splice(index, 1);
+ }
+
+ /**
+ * Invokes the event and calls all event handler functions currently registered on the event.
+ *
+ * @param argument The argument for the event. The argument will be passed to all registered event handler functions.
+ */
+ public invoke = (argument?: TEventArg): void => {
+ this.eventHandlers.forEach((eventHandler: (arg?: TEventArg) => void, index: number, array: Array<(arg: TEventArg) => void>): void => {
+ eventHandler(argument);
+ });
+ }
+
+ private eventHandlers: Array<(arg?: TEventArg) => void>;
+
+}
diff --git a/sdnr/wt-odlux/odlux/framework/src/components/errorDisplay.tsx b/sdnr/wt-odlux/odlux/framework/src/components/errorDisplay.tsx
new file mode 100644
index 000000000..d41d82687
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/components/errorDisplay.tsx
@@ -0,0 +1,131 @@
+/**
+ * ============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 { Theme } from '@mui/material/styles';
+
+import { WithStyles } from '@mui/styles';
+import withStyles from '@mui/styles/withStyles';
+import createStyles from '@mui/styles/createStyles';
+
+import Modal from '@mui/material/Modal';
+import Button from '@mui/material/Button';
+import Card from '@mui/material/Card';
+import CardActions from '@mui/material/CardActions';
+import CardContent from '@mui/material/CardContent';
+import Typography from '@mui/material/Typography';
+
+import { ClearErrorInfoAction, RemoveErrorInfoAction } from '../actions/errorActions';
+
+import { connect, Connect } from '../flux/connect';
+
+const styles = (theme: Theme) => createStyles({
+ modal: {
+ display: "flex",
+ alignItems: "center",
+ justifyContent: "center",
+ },
+ paper: {
+ width: theme.spacing(50),
+ backgroundColor: theme.palette.background.paper,
+ boxShadow: theme.shadows[5],
+ padding: theme.spacing(4),
+ },
+ card: {
+ minWidth: 275,
+ },
+ bullet: {
+ display: 'inline-block',
+ margin: '0 2px',
+ transform: 'scale(0.8)',
+ },
+ title: {
+ marginBottom: 16,
+ fontSize: 14,
+ },
+ pos: {
+ marginBottom: 12,
+ },
+});
+
+type ErrorDisplayProps = WithStyles<typeof styles> & Connect;
+
+// function getModalStyle() {
+// const top = 50 + rand();
+// const left = 50 + rand();
+
+// return {
+// top: `${ top }%`,
+// left: `${ left }%`,
+// transform: `translate(-${ top }%, -${ left }%)`,
+// };
+// }
+
+/**
+ * Represents a component for formatting and displaying errors.
+ */
+class ErrorDisplayComponent extends React.Component<ErrorDisplayProps> {
+ constructor(props: ErrorDisplayProps) {
+ super(props);
+ }
+
+ render(): JSX.Element {
+ const { classes, state } = this.props;
+ const errorInfo = state.framework.applicationState.errors.length && state.framework.applicationState.errors[state.framework.applicationState.errors.length - 1];
+
+ return (
+ <Modal className={classes.modal}
+ aria-labelledby="simple-modal-title"
+ aria-describedby="simple-modal-description"
+ open={state.framework.applicationState.errors && state.framework.applicationState.errors.length > 0}
+ onClose={() => this.props.dispatch(new ClearErrorInfoAction())}
+ >
+ {errorInfo &&
+ <div className={classes.paper}>
+ <Card className={classes.card}>
+ <CardContent>
+ <Typography className={classes.title} color="textSecondary">
+ {errorInfo.title != null ? errorInfo.title : "Something went wrong."}
+ </Typography>
+ <Typography variant="h5" component="h2">
+ {errorInfo.error && errorInfo.error.toString()}
+ </Typography>
+ <Typography className={classes.pos} color="textSecondary">
+ {errorInfo.message && errorInfo.message.toString()}
+ </Typography>
+ <Typography component="p">
+ {errorInfo.info && errorInfo.info.componentStack && errorInfo.info.componentStack.split('\n').map(line => {
+ return [line, <br />];
+ })}
+ {errorInfo.info && errorInfo.info.extra && errorInfo.info.extra.split('\n').map(line => {
+ return [line, <br />];
+ })}
+ </Typography>
+ </CardContent>
+ <CardActions>
+ <Button color="inherit" size="small" onClick={() => this.props.dispatch(new RemoveErrorInfoAction(errorInfo))} >Close</Button>
+ </CardActions>
+ </Card>
+ </div> || <div></div>
+ }
+ </Modal>
+ );
+ }
+}
+
+export const ErrorDisplay = withStyles(styles)(connect()(ErrorDisplayComponent));
+export default ErrorDisplay; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/components/icons/menuIcon.tsx b/sdnr/wt-odlux/odlux/framework/src/components/icons/menuIcon.tsx
new file mode 100644
index 000000000..0d7d734c9
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/components/icons/menuIcon.tsx
@@ -0,0 +1,29 @@
+import React from 'react';
+
+type MenuIconPropsBase = {
+ className?: string;
+ size?: number | string;
+};
+
+type MenuIconPropsWithColor = MenuIconPropsBase & {
+ color: string;
+};
+
+type MenuIconProps = MenuIconPropsBase | MenuIconPropsWithColor;
+
+const MenuIcon = (props: MenuIconProps) => {
+ const { className, size = '30px' } = props;
+ const color = 'color' in props ? props.color : '#36A9E1';
+
+ return (
+ <svg className={className} width={size} height={size} viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink">
+ <path fill={color} d="M4,10h24c1.104,0,2-0.896,2-2s-0.896-2-2-2H4C2.896,6,2,6.896,2,8S2.896,10,4,10z" />
+ <path fill={color} d="M28,14H4c-1.104,0-2,0.896-2,2 s0.896,2,2,2h24c1.104,0,2-0.896,2-2S29.104,14,28,14z" />
+ <path fill={color} d="M28,22H4c-1.104,0-2,0.896-2,2s0.896,2,2,2h24c1.104,0,2-0.896,2-2 S29.104,22,28,22z" />
+ </svg>
+ );
+};
+
+MenuIcon.defaultName = 'MenuIcon';
+
+export default MenuIcon; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/components/logo.tsx b/sdnr/wt-odlux/odlux/framework/src/components/logo.tsx
new file mode 100644
index 000000000..f2bb1f575
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/components/logo.tsx
@@ -0,0 +1,103 @@
+/**
+ * ============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==========================================================================
+ */
+/******************************************************************************
+ * Copyright 2018 highstreet technologies GmbH
+ *
+ * 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.
+ *****************************************************************************/
+
+import * as React from 'react';
+import { withRouter, RouteComponentProps } from 'react-router-dom';
+
+import { Theme } from '@mui/material/styles'; // infra for styling
+
+
+import { WithStyles } from '@mui/styles';
+import withStyles from '@mui/styles/withStyles';
+import createStyles from '@mui/styles/createStyles';
+
+
+const defaultLogo = require('../assets/icons/ht.Connect.svg');
+
+const styles = (theme: Theme) => createStyles({
+ headerLogo: {
+ backgroundImage: "url(" + (theme.design && theme.design.url || defaultLogo) + ")",
+ backgroundColor: theme.palette.primary.main,
+ backgroundRepeat: "no-repeat",
+ backgroundSize: "auto " + (theme.design && theme.design.logoHeight || 70) + "px",
+ height: theme.design && theme.design.logoHeight || 70,
+ width: theme.design ? theme.design.width / theme.design.height * theme.design.logoHeight : 220
+ }
+});
+
+type LogoProps = RouteComponentProps<{ id: string }> & WithStyles<typeof styles>;
+interface ILogoState {
+ windowWidth: number
+}
+
+class LogoComponent extends React.Component<LogoProps, ILogoState> {
+
+ private hideLogoWhenWindowWidthIsLower: number = 800;
+
+ constructor(props: LogoProps) {
+ super(props);
+ this.state = {
+ windowWidth: 0
+ };
+ this.updateWindowDimensions = this.updateWindowDimensions.bind(this);
+ }
+
+ componentDidMount(): void {
+ this.updateWindowDimensions();
+ window.addEventListener('resize', this.updateWindowDimensions);
+ };
+ componentWillUnmount(): void {
+ window.removeEventListener('resize', this.updateWindowDimensions);
+ };
+ updateWindowDimensions(): void {
+ this.setState({ windowWidth: window.innerWidth });
+ }
+
+ render(): JSX.Element {
+ let div: JSX.Element = <div />;
+ if (this.state.windowWidth >= this.hideLogoWhenWindowWidthIsLower) {
+ div = <div className={this.props.classes.headerLogo} />;
+ } else {
+ console.info([
+ "Logo hidden, because browser window width (",
+ this.state.windowWidth,
+ "px) is lower threshold (",
+ this.hideLogoWhenWindowWidthIsLower,
+ "px)."].join(''));
+ }
+ return div;
+ }
+}
+
+export const Logo = withStyles(styles)(withRouter(LogoComponent));
+export default Logo;
diff --git a/sdnr/wt-odlux/odlux/framework/src/components/material-table/columnModel.ts b/sdnr/wt-odlux/odlux/framework/src/components/material-table/columnModel.ts
new file mode 100644
index 000000000..3ed313497
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/components/material-table/columnModel.ts
@@ -0,0 +1,56 @@
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ */
+
+import * as React from 'react';
+
+export enum ColumnType {
+ text,
+ numeric,
+ boolean,
+ date,
+ custom
+}
+
+type CustomControl<TData> = {
+ className?: string;
+ style?: React.CSSProperties;
+ rowData: TData;
+}
+
+export type ColumnModel<TData> = {
+ title?: string;
+ disablePadding?: boolean;
+ width?: string | number ;
+ className?: string;
+ hide?: boolean;
+ style?: React.CSSProperties;
+ align?: 'inherit' | 'left' | 'center' | 'right' | 'justify';
+ disableSorting?: boolean;
+ disableFilter?: boolean;
+} & ({
+ property: string;
+ type: ColumnType.custom;
+ customControl: React.ComponentType<CustomControl<TData>>;
+} | {
+ property: keyof TData;
+ type: ColumnType.boolean;
+ labels?: { "true": string, "false": string };
+} | {
+ property: keyof TData;
+ type?: ColumnType.numeric | ColumnType.text | ColumnType.date;
+}); \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/components/material-table/index.tsx b/sdnr/wt-odlux/odlux/framework/src/components/material-table/index.tsx
new file mode 100644
index 000000000..c1a5005d4
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/components/material-table/index.tsx
@@ -0,0 +1,707 @@
+/**
+ * ============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 } from '@mui/material/styles';
+
+import { WithStyles } from '@mui/styles';
+import withStyles from '@mui/styles/withStyles';
+import createStyles from '@mui/styles/createStyles';
+
+import Table from '@mui/material/Table';
+import TableBody from '@mui/material/TableBody';
+import TableCell from '@mui/material/TableCell';
+import TableContainer from '@mui/material/TableContainer';
+import TablePagination from '@mui/material/TablePagination';
+import TableRow from '@mui/material/TableRow';
+import Paper from '@mui/material/Paper';
+import Checkbox from '@mui/material/Checkbox';
+
+import { TableToolbar } from './tableToolbar';
+import { EnhancedTableHead } from './tableHead';
+import { EnhancedTableFilter } from './tableFilter';
+
+import { ColumnModel, ColumnType } from './columnModel';
+import { Menu, Typography } from '@mui/material';
+import { DistributiveOmit } from '@mui/types';
+
+import makeStyles from '@mui/styles/makeStyles';
+
+import { SvgIconProps } from '@mui/material/SvgIcon';
+
+import { DividerTypeMap } from '@mui/material/Divider';
+import { MenuItemProps } from '@mui/material/MenuItem';
+import { flexbox } from '@mui/system';
+import { RowDisabled } from './utilities';
+import { toAriaLabel } from '../../utilities/yangHelper';
+export { ColumnModel, ColumnType } from './columnModel';
+
+type propType = string | number | null | undefined | (string | number)[];
+type dataType = { [prop: string]: propType };
+type resultType<TData = dataType> = { page: number, total: number, rows: TData[] };
+
+export type DataCallback<TData = dataType> = (page?: number, rowsPerPage?: number, orderBy?: string | null, order?: 'asc' | 'desc' | null, filter?: { [property: string]: string }) => resultType<TData> | Promise<resultType<TData>>;
+
+function regExpEscape(s: string) {
+ return s.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&');
+}
+
+function wildcardCheck(input: string, pattern: string) {
+ if (!pattern) return true;
+ const regex = new RegExp(
+ (!pattern.startsWith('*') ? '^' : '') +
+ pattern.split(/\*+/).map(p => p.split(/\?+/).map(regExpEscape).join('.')).join('.*') +
+ (!pattern.endsWith('*') ? '$' : '')
+ );
+ return input.match(regex) !== null && input.match(regex)!.length >= 1;
+}
+
+function desc(a: dataType, b: dataType, orderBy: string) {
+ if ((b[orderBy] || "") < (a[orderBy] || "")) {
+ return -1;
+ }
+ if ((b[orderBy] || "") > (a[orderBy] || "")) {
+ return 1;
+ }
+ return 0;
+}
+
+function stableSort(array: dataType[], cmp: (a: dataType, b: dataType) => number) {
+ const stabilizedThis = array.map((el, index) => [el, index]) as [dataType, number][];
+ stabilizedThis.sort((a, b) => {
+ const order = cmp(a[0], b[0]);
+ if (order !== 0) return order;
+ return a[1] - b[1];
+ });
+ return stabilizedThis.map(el => el[0]);
+}
+
+function getSorting(order: 'asc' | 'desc' | null, orderBy: string) {
+ return order === 'desc' ? (a: dataType, b: dataType) => desc(a, b, orderBy) : (a: dataType, b: dataType) => -desc(a, b, orderBy);
+}
+
+const styles = (theme: Theme) => createStyles({
+ root: {
+ width: '100%',
+ overflow: "hidden",
+ marginTop: theme.spacing(3),
+ position: "relative",
+ boxSizing: "border-box",
+ display: "flex",
+ flexDirection: "column",
+ },
+ container: {
+ flex: "1 1 100%"
+ },
+ pagination: {
+ overflow: "hidden",
+ minHeight: "52px"
+ }
+});
+
+const useTableRowExtStyles = makeStyles((theme: Theme) => createStyles({
+ disabled: {
+ color: "rgba(180, 180, 180, 0.7)",
+ },
+}));
+
+type GetStatelessComponentProps<T> = T extends (props: infer P & { children?: React.ReactNode }) => any ? P : any;
+type TableRowExtProps = GetStatelessComponentProps<typeof TableRow> & { disabled: boolean };
+const TableRowExt : React.FC<TableRowExtProps> = (props) => {
+ const [disabled, setDisabled] = React.useState(true);
+ const classes = useTableRowExtStyles();
+
+ const onMouseDown = (ev: React.MouseEvent<HTMLElement>) => {
+ if (ev.button ===1){
+ setDisabled(!disabled);
+ ev.preventDefault();
+ ev.stopPropagation();
+ } else if (props.disabled && disabled) {
+ ev.preventDefault();
+ ev.stopPropagation();
+ }
+ };
+
+ return (
+ <TableRow {...{...props, color: props.disabled && disabled ? '#a0a0a0' : undefined , className: props.disabled && disabled ? classes.disabled : '', onMouseDown, onContextMenu: props.disabled && disabled ? onMouseDown : props.onContextMenu } } />
+ );
+};
+
+export type MaterialTableComponentState<TData = {}> = {
+ order: 'asc' | 'desc';
+ orderBy: string | null;
+ selected: any[] | null;
+ rows: TData[];
+ total: number;
+ page: number;
+ rowsPerPage: number;
+ loading: boolean;
+ showFilter: boolean;
+ hiddenColumns: string[];
+ filter: { [property: string]: string };
+};
+
+export type TableApi = { forceRefresh?: () => Promise<void> };
+
+type MaterialTableComponentBaseProps<TData> = WithStyles<typeof styles> & {
+ className?: string;
+ columns: ColumnModel<TData>[];
+ idProperty: keyof TData | ((data: TData) => React.Key);
+
+ //Note: used to save settings as well. Must be unique across apps. Null tableIds will not get saved to the settings
+ tableId: string | null;
+ isPopup?: boolean;
+ title?: string;
+ stickyHeader?: boolean;
+ allowHtmlHeader?: boolean;
+ defaultSortOrder?: 'asc' | 'desc';
+ defaultSortColumn?: keyof TData;
+ enableSelection?: boolean;
+ disableSorting?: boolean;
+ disableFilter?: boolean;
+ customActionButtons?: { icon: React.ComponentType<SvgIconProps>, tooltip?: string, ariaLabel: string, onClick: () => void, disabled?: boolean }[];
+ onHandleClick?(event: React.MouseEvent<HTMLTableRowElement>, rowData: TData): void;
+ createContextMenu?: (row: TData) => React.ReactElement<MenuItemProps | DividerTypeMap<{}, "hr">, React.ComponentType<MenuItemProps | DividerTypeMap<{}, "hr">>>[];
+};
+
+type MaterialTableComponentPropsWithRows<TData = {}> = MaterialTableComponentBaseProps<TData> & { rows: TData[]; asynchronus?: boolean; };
+type MaterialTableComponentPropsWithRequestData<TData = {}> = MaterialTableComponentBaseProps<TData> & { onRequestData: DataCallback; tableApi?: TableApi; };
+type MaterialTableComponentPropsWithExternalState<TData = {}> = MaterialTableComponentBaseProps<TData> & MaterialTableComponentState & {
+ onToggleFilter: () => void;
+ onFilterChanged: (property: string, filterTerm: string) => void;
+ onHandleChangePage: (page: number) => void;
+ onHandleChangeRowsPerPage: (rowsPerPage: number | null) => void;
+ onHandleRequestSort: (property: string) => void;
+ onHideColumns : (columnNames: string[]) => void
+ onShowColumns: (columnNames: string[]) => void
+};
+
+type MaterialTableComponentProps<TData = {}> =
+ MaterialTableComponentPropsWithRows<TData> |
+ MaterialTableComponentPropsWithRequestData<TData> |
+ MaterialTableComponentPropsWithExternalState<TData>;
+
+function isMaterialTableComponentPropsWithRows(props: MaterialTableComponentProps): props is MaterialTableComponentPropsWithRows {
+ return (props as MaterialTableComponentPropsWithRows).rows !== undefined && (props as MaterialTableComponentPropsWithRows).rows instanceof Array;
+}
+
+function isMaterialTableComponentPropsWithRequestData(props: MaterialTableComponentProps): props is MaterialTableComponentPropsWithRequestData {
+ return (props as MaterialTableComponentPropsWithRequestData).onRequestData !== undefined && (props as MaterialTableComponentPropsWithRequestData).onRequestData instanceof Function;
+}
+
+function isMaterialTableComponentPropsWithRowsAndRequestData(props: MaterialTableComponentProps): props is MaterialTableComponentPropsWithExternalState {
+ const propsWithExternalState = (props as MaterialTableComponentPropsWithExternalState)
+ return propsWithExternalState.onFilterChanged instanceof Function ||
+ propsWithExternalState.onHandleChangePage instanceof Function ||
+ propsWithExternalState.onHandleChangeRowsPerPage instanceof Function ||
+ propsWithExternalState.onToggleFilter instanceof Function ||
+ propsWithExternalState.onHideColumns instanceof Function ||
+ propsWithExternalState.onHandleRequestSort instanceof Function
+}
+
+// get settings in here!
+
+
+class MaterialTableComponent<TData extends {} = {}> extends React.Component<MaterialTableComponentProps, MaterialTableComponentState & { contextMenuInfo: { index: number; mouseX?: number; mouseY?: number }; }> {
+
+ constructor(props: MaterialTableComponentProps) {
+ super(props);
+
+
+ const page = isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.page : 0;
+ const rowsPerPage = isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.rowsPerPage || 10 : 10;
+
+ this.state = {
+ contextMenuInfo: { index: -1 },
+ filter: isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.filter || {} : {},
+ showFilter: isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.showFilter : false,
+ loading: isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.loading : false,
+ order: isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.order : this.props.defaultSortOrder || 'asc',
+ orderBy: isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.orderBy : this.props.defaultSortColumn || null,
+ selected: isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.selected : null,
+ rows: isMaterialTableComponentPropsWithRows(this.props) && this.props.rows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) || [],
+ total: isMaterialTableComponentPropsWithRows(this.props) && this.props.rows.length || 0,
+ hiddenColumns: isMaterialTableComponentPropsWithRowsAndRequestData(this.props) && this.props.hiddenColumns || [],
+ page,
+ rowsPerPage,
+ };
+
+ if (isMaterialTableComponentPropsWithRequestData(this.props)) {
+ this.update();
+
+ if (this.props.tableApi) {
+ this.props.tableApi.forceRefresh = () => this.update();
+ }
+ }
+ }
+ render(): JSX.Element {
+ const { classes, columns, allowHtmlHeader } = this.props;
+ const { rows, total: rowCount, order, orderBy, selected, rowsPerPage, page, showFilter, filter } = this.state;
+ const emptyRows = rowsPerPage - Math.min(rowsPerPage, rowCount - page * rowsPerPage);
+ const getId = typeof this.props.idProperty !== "function" ? (data: TData) => ((data as { [key: string]: any })[this.props.idProperty as any as string] as string | number) : this.props.idProperty;
+ const toggleFilter = isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.onToggleFilter : () => { !this.props.disableFilter && this.setState({ showFilter: !showFilter }, this.update) }
+
+ const hideColumns = isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.onHideColumns : (data: string[]) => { const newArray = [...new Set([...this.state.hiddenColumns, ...data])]; this.setState({hiddenColumns:newArray}); }
+ const showColumns = isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.onShowColumns : (data: string[]) => { const newArray = this.state.hiddenColumns.filter(el=> !data.includes(el)); this.setState({hiddenColumns:newArray}); }
+
+ const allColumnsHidden = this.props.columns.length === this.state.hiddenColumns.length;
+ return (
+ <Paper className={this.props.className ? `${classes.root} ${this.props.className}` : classes.root}>
+ <TableContainer className={classes.container}>
+ <TableToolbar tableId={this.props.tableId} numSelected={selected && selected.length} title={this.props.title} customActionButtons={this.props.customActionButtons} onExportToCsv={this.exportToCsv}
+ onToggleFilter={toggleFilter}
+ columns={columns}
+ onHideColumns={hideColumns}
+ onShowColumns={showColumns} />
+ <Table padding="normal" aria-label={this.props.tableId ? this.props.tableId : 'tableTitle'} stickyHeader={this.props.stickyHeader || false} >
+ <EnhancedTableHead
+ allowHtmlHeader={allowHtmlHeader || false}
+ columns={columns}
+ numSelected={selected && selected.length}
+ order={order}
+ orderBy={orderBy}
+ onSelectAllClick={this.handleSelectAllClick}
+ onRequestSort={this.onHandleRequestSort}
+ rowCount={rows.length}
+ enableSelection={this.props.enableSelection}
+ hiddenColumns={this.state.hiddenColumns}
+ />
+ <TableBody>
+ {showFilter && <EnhancedTableFilter columns={columns} hiddenColumns={this.state.hiddenColumns} filter={filter} onFilterChanged={this.onFilterChanged} enableSelection={this.props.enableSelection} /> || null}
+
+ {allColumnsHidden ? <Typography variant="body1" textAlign="center">All columns of this table are hidden.</Typography> :
+
+ rows // may need ordering here
+ .map((entry: TData & { [RowDisabled]?: boolean, [kex: string]: any }, index) => {
+ const entryId = getId(entry);
+ const contextMenu = (this.props.createContextMenu && this.state.contextMenuInfo.index === index && this.props.createContextMenu(entry)) || null;
+ const isSelected = this.isSelected(entryId) || this.state.contextMenuInfo.index === index;
+ return (
+ <TableRowExt
+ hover
+ onClick={event => {
+ if (this.props.createContextMenu) {
+ this.setState({
+ contextMenuInfo: {
+ index: -1
+ }
+ });
+ }
+ this.handleClick(event, entry, entryId);
+ }}
+ onContextMenu={event => {
+ if (this.props.createContextMenu) {
+ event.preventDefault();
+ event.stopPropagation();
+ this.setState({ contextMenuInfo: { index, mouseX: event.clientX - 2, mouseY: event.clientY - 4 } });
+ }
+ }}
+ role="checkbox"
+ aria-checked={isSelected}
+ aria-label="table-row"
+ tabIndex={-1}
+ key={entryId}
+ selected={isSelected}
+ disabled={entry[RowDisabled] || false}
+ >
+ {this.props.enableSelection
+ ? <TableCell padding="checkbox" style={{ width: "50px", color: entry[RowDisabled] || false ? "inherit" : undefined } }>
+ <Checkbox color='secondary' checked={isSelected} />
+ </TableCell>
+ : null
+ }
+ {
+
+ this.props.columns.map(
+ col => {
+ const style = col.width ? { width: col.width } : {};
+ const tableCell = (
+
+ <TableCell style={ entry[RowDisabled] || false ? { ...style, color: "inherit" } : style } aria-label={col.title? toAriaLabel(col.title) : toAriaLabel(col.property)} key={col.property} align={col.type === ColumnType.numeric && !col.align ? "right" : col.align} >
+ {col.type === ColumnType.custom && col.customControl
+ ? <col.customControl className={col.className} style={col.style} rowData={entry} />
+ : col.type === ColumnType.boolean
+ ? <span className={col.className} style={col.style}>{col.labels ? col.labels[entry[col.property] ? "true" : "false"] : String(entry[col.property])}</span>
+ : <span className={col.className} style={col.style}>{String(entry[col.property])}</span>
+ }
+ </TableCell>
+ );
+
+ //show column if...
+ const showColumn = !this.state.hiddenColumns.includes(col.property);
+ return showColumn && tableCell
+ }
+ )
+ }
+ {<Menu open={!!contextMenu} onClose={() => this.setState({ contextMenuInfo: { index: -1 } })} anchorReference="anchorPosition" keepMounted
+ anchorPosition={this.state.contextMenuInfo.mouseY != null && this.state.contextMenuInfo.mouseX != null ? { top: this.state.contextMenuInfo.mouseY, left: this.state.contextMenuInfo.mouseX } : undefined}>
+ {contextMenu}
+ </Menu> || null}
+ </TableRowExt>
+ );
+ })}
+ {emptyRows > 0 && (
+ <TableRow style={{ height: 49 * emptyRows }}>
+ <TableCell colSpan={this.props.columns.length} />
+ </TableRow>
+ )}
+ </TableBody>
+ </Table>
+ </TableContainer>
+ <TablePagination className={classes.pagination}
+ rowsPerPageOptions={[5, 10, 20, 50]}
+ component="div"
+ count={rowCount}
+ rowsPerPage={rowsPerPage}
+ page={page}
+ aria-label={this.props.isPopup ? "popup-table-pagination-footer" : "table-pagination-footer" }
+ backIconButtonProps={{
+ 'aria-label': this.props.isPopup ? 'popup-previous-page' : 'previous-page',
+ }}
+ nextIconButtonProps={{
+ 'aria-label': this.props.isPopup ? 'popup-next-page': 'next-page',
+ }}
+ onPageChange={this.onHandleChangePage}
+ onRowsPerPageChange={this.onHandleChangeRowsPerPage}
+ />
+ </Paper>
+ );
+ }
+
+ static getDerivedStateFromProps(props: MaterialTableComponentProps, state: MaterialTableComponentState & { _rawRows: {}[] }): MaterialTableComponentState & { _rawRows: {}[] } {
+
+ if (isMaterialTableComponentPropsWithRowsAndRequestData(props)) {
+ return {
+ ...state,
+ rows: props.rows,
+ total: props.total,
+ orderBy: props.orderBy,
+ order: props.order,
+ filter: props.filter,
+ loading: props.loading,
+ showFilter: props.showFilter,
+ page: props.page,
+ hiddenColumns: props.hiddenColumns,
+ rowsPerPage: props.rowsPerPage
+ }
+ } else if (isMaterialTableComponentPropsWithRows(props) && props.asynchronus && state._rawRows !== props.rows) {
+ const newState = MaterialTableComponent.updateRows(props, state);
+ return {
+ ...state,
+ ...newState,
+ _rawRows: props.rows || []
+ };
+ }
+ return state;
+ }
+
+ private static updateRows(props: MaterialTableComponentPropsWithRows, state: MaterialTableComponentState): { rows: {}[], total: number, page: number } {
+
+ let data = [...(props.rows as dataType[] || [])];
+ const columns = props.columns;
+
+ const { page, rowsPerPage, order, orderBy, filter } = state;
+
+ try {
+ if (state.showFilter) {
+ Object.keys(filter).forEach(prop => {
+ const column = columns.find(c => c.property === prop);
+ const filterExpression = filter[prop];
+
+ if (!column) throw new Error("Filter for not existing column found.");
+
+ if (filterExpression != null) {
+ data = data.filter((val) => {
+ const dataValue = val[prop];
+
+ if (dataValue != null) {
+
+ if (column.type === ColumnType.boolean) {
+
+ const boolDataValue = JSON.parse(String(dataValue).toLowerCase());
+ const boolFilterExpression = JSON.parse(String(filterExpression).toLowerCase());
+ return boolDataValue == boolFilterExpression;
+
+ } else if (column.type === ColumnType.text) {
+
+ const valueAsString = String(dataValue);
+ const filterExpressionAsString = String(filterExpression).trim();
+ if (filterExpressionAsString.length === 0) return true;
+ return wildcardCheck(valueAsString, filterExpressionAsString);
+
+ } else if (column.type === ColumnType.numeric){
+
+ const valueAsNumber = Number(dataValue);
+ const filterExpressionAsString = String(filterExpression).trim();
+ if (filterExpressionAsString.length === 0 || isNaN(valueAsNumber)) return true;
+
+ if (filterExpressionAsString.startsWith('>=')) {
+ return valueAsNumber >= Number(filterExpressionAsString.substring(2).trim());
+ } else if (filterExpressionAsString.startsWith('<=')) {
+ return valueAsNumber <= Number(filterExpressionAsString.substring(2).trim());
+ } else if (filterExpressionAsString.startsWith('>')) {
+ return valueAsNumber > Number(filterExpressionAsString.substring(1).trim());
+ } else if (filterExpressionAsString.startsWith('<')) {
+ return valueAsNumber < Number(filterExpressionAsString.substring(1).trim());
+ }
+ } else if (column.type === ColumnType.date){
+ const valueAsString = String(dataValue);
+
+ const convertToDate = (valueAsString: string) => {
+ // time value needs to be padded
+ const hasTimeValue = /T\d{2,2}/.test(valueAsString);
+ const indexCollon = valueAsString.indexOf(':');
+ if (hasTimeValue && (indexCollon === -1 || indexCollon >= valueAsString.length-2)) {
+ valueAsString = indexCollon === -1
+ ? valueAsString + ":00"
+ : indexCollon === valueAsString.length-1
+ ? valueAsString + "00"
+ : valueAsString += "0"
+ }
+ return new Date(Date.parse(valueAsString));
+ };
+
+ // @ts-ignore
+ const valueAsDate = new Date(Date.parse(dataValue));
+ const filterExpressionAsString = String(filterExpression).trim();
+
+ if (filterExpressionAsString.startsWith('>=')) {
+ return valueAsDate >= convertToDate(filterExpressionAsString.substring(2).trim());
+ } else if (filterExpressionAsString.startsWith('<=')) {
+ return valueAsDate <= convertToDate(filterExpressionAsString.substring(2).trim());
+ } else if (filterExpressionAsString.startsWith('>')) {
+ return valueAsDate > convertToDate(filterExpressionAsString.substring(1).trim());
+ } else if (filterExpressionAsString.startsWith('<')) {
+ return valueAsDate < convertToDate(filterExpressionAsString.substring(1).trim());
+ }
+
+
+ if (filterExpressionAsString.length === 0) return true;
+ return wildcardCheck(valueAsString, filterExpressionAsString);
+
+ }
+ }
+
+ return (dataValue == filterExpression)
+ });
+ };
+ });
+ }
+
+ const rowCount = data.length;
+
+ if (page > 0 && rowsPerPage * page > rowCount) { //if result is smaller than the currently shown page, new search and repaginate
+ let newPage = Math.floor(rowCount / rowsPerPage);
+ return {
+ rows: data,
+ total: rowCount,
+ page: newPage
+ };
+ } else {
+ data = (orderBy && order
+ ? stableSort(data, getSorting(order, orderBy))
+ : data).slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage);
+
+ return {
+ rows: data,
+ total: rowCount,
+ page: page
+ };
+ }
+
+
+ } catch (e) {
+ console.error(e);
+ return {
+ rows: [],
+ total: 0,
+ page: page
+ }
+ }
+ }
+
+ private async update() {
+ if (isMaterialTableComponentPropsWithRequestData(this.props)) {
+ const response = await Promise.resolve(
+ this.props.onRequestData(
+ this.state.page, this.state.rowsPerPage, this.state.orderBy, this.state.order, this.state.showFilter && this.state.filter || {})
+ );
+ this.setState(response);
+ } else {
+ let updateResult = MaterialTableComponent.updateRows(this.props, this.state);
+ this.setState(updateResult);
+ }
+ }
+
+ private onFilterChanged = (property: string, filterTerm: string) => {
+ if (isMaterialTableComponentPropsWithRowsAndRequestData(this.props)) {
+ this.props.onFilterChanged(property, filterTerm);
+ return;
+ }
+ if (this.props.disableFilter) return;
+ const colDefinition = this.props.columns && this.props.columns.find(col => col.property === property);
+ if (colDefinition && colDefinition.disableFilter) return;
+
+ const filter = { ...this.state.filter, [property]: filterTerm };
+ this.setState({
+ filter
+ }, this.update);
+ };
+
+ private onHandleRequestSort = (event: React.SyntheticEvent, property: string) => {
+ if (isMaterialTableComponentPropsWithRowsAndRequestData(this.props)) {
+ this.props.onHandleRequestSort(property);
+ return;
+ }
+ if (this.props.disableSorting) return;
+ const colDefinition = this.props.columns && this.props.columns.find(col => col.property === property);
+ if (colDefinition && colDefinition.disableSorting) return;
+
+ const orderBy = this.state.orderBy === property && this.state.order === 'desc' ? null : property;
+ const order = this.state.orderBy === property && this.state.order === 'asc' ? 'desc' : 'asc';
+ this.setState({
+ order,
+ orderBy
+ }, this.update);
+ };
+
+ handleSelectAllClick: () => {};
+
+ private onHandleChangePage = (event: any | null, page: number) => {
+ if (isMaterialTableComponentPropsWithRowsAndRequestData(this.props)) {
+ this.props.onHandleChangePage(page);
+ return;
+ }
+ this.setState({
+ page
+ }, this.update);
+ };
+
+ private onHandleChangeRowsPerPage = (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
+ if (isMaterialTableComponentPropsWithRowsAndRequestData(this.props)) {
+ this.props.onHandleChangeRowsPerPage(+(event && event.target.value));
+ return;
+ }
+ const rowsPerPage = +(event && event.target.value);
+ if (rowsPerPage && rowsPerPage > 0) {
+ this.setState({
+ rowsPerPage
+ }, this.update);
+ }
+ };
+
+ private isSelected(id: string | number): boolean {
+ let selected = this.state.selected || [];
+ const selectedIndex = selected.indexOf(id);
+ return (selectedIndex > -1);
+ }
+
+ private handleClick(event: any, rowData: TData, id: string | number): void {
+ if (this.props.onHandleClick instanceof Function) {
+ this.props.onHandleClick(event, rowData);
+ return;
+ }
+ if (!this.props.enableSelection) {
+ return;
+ }
+ let selected = this.state.selected || [];
+ const selectedIndex = selected.indexOf(id);
+ if (selectedIndex > -1) {
+ selected = [
+ ...selected.slice(0, selectedIndex),
+ ...selected.slice(selectedIndex + 1)
+ ];
+ } else {
+ selected = [
+ ...selected,
+ id
+ ];
+ }
+ this.setState({
+ selected
+ });
+ }
+
+
+ private exportToCsv = async () => {
+ let file;
+ let data: dataType[] | null = null;
+ let csv: string[] = [];
+
+ if (isMaterialTableComponentPropsWithRequestData(this.props)) {
+ // table with extra request handler
+ this.setState({ loading: true });
+ const result = await Promise.resolve(
+ this.props.onRequestData(0, 1000, this.state.orderBy, this.state.order, this.state.showFilter && this.state.filter || {})
+ );
+ data = result.rows;
+ this.setState({ loading: true });
+ } else if (isMaterialTableComponentPropsWithRowsAndRequestData(this.props)) {
+ // table with generated handlers note: exports data shown on current page
+ data = this.props.rows;
+ }
+ else {
+ // table with local data
+ data = MaterialTableComponent.updateRows(this.props, this.state).rows;
+ }
+
+ if (data && data.length > 0) {
+ csv.push(this.props.columns.map(col => col.title || col.property).join(',') + "\r\n");
+ this.state.rows && this.state.rows.forEach((row: any) => {
+ csv.push(this.props.columns.map(col => row[col.property]).join(',') + "\r\n");
+ });
+ const properties = { type: "text/csv;charset=utf-8" }; // Specify the file's mime-type.
+ try {
+ // Specify the filename using the File constructor, but ...
+ file = new File(csv, "export.csv", properties);
+ } catch (e) {
+ // ... fall back to the Blob constructor if that isn't supported.
+ file = new Blob(csv, properties);
+ }
+ }
+ if (!file) return;
+ var reader = new FileReader();
+ reader.onload = function (e) {
+ const dataUri = reader.result as any;
+ const link = document.createElement("a");
+ if (typeof link.download === 'string') {
+ link.href = dataUri;
+ link.download = "export.csv";
+
+ //Firefox requires the link to be in the body
+ document.body.appendChild(link);
+
+ //simulate click
+ link.click();
+
+ //remove the link when done
+ document.body.removeChild(link);
+ } else {
+ window.open(dataUri);
+ }
+ }
+ reader.readAsDataURL(file);
+
+ // const url = URL.createObjectURL(file);
+ // window.location.replace(url);
+ }
+}
+
+export type MaterialTableCtorType<TData extends {} = {}> = new () => React.Component<DistributiveOmit<MaterialTableComponentProps<TData>, 'classes'>>;
+
+export const MaterialTable = withStyles(styles)(MaterialTableComponent);
+export default MaterialTable; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/components/material-table/showColumnDialog.tsx b/sdnr/wt-odlux/odlux/framework/src/components/material-table/showColumnDialog.tsx
new file mode 100644
index 000000000..ab0d465e7
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/components/material-table/showColumnDialog.tsx
@@ -0,0 +1,188 @@
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2022 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 { Button, FormControlLabel, Popover, Switch, Typography } from '@mui/material';
+import { connect, Connect, IDispatcher } from '../../flux/connect';
+
+import { ColumnModel } from './columnModel';
+import { IApplicationStoreState } from '../../store/applicationStore';
+import { TableSettingsColumn } from '../../models/settings';
+import { updateTableSettings } from '../../actions/settingsAction';
+
+const mapStateToProps = (state: IApplicationStoreState) => ({
+ settings: state.framework.applicationState.settings,
+ settingsDoneLoading: state.framework.applicationState.settings.isInitialLoadDone
+});
+
+const mapDispatchToProps = (dispatcher: IDispatcher) => ({
+ saveToSettings: (tableName: string, columns: TableSettingsColumn[]) => dispatcher.dispatch(updateTableSettings(tableName, columns))
+})
+
+type DialogProps = {
+ columns: ColumnModel<{}>[],
+ settingsName: string | null,
+ anchorEl: HTMLElement | null;
+ hideColumns: (columnNames: string[]) => void
+ showColumns: (columnNames: string[]) => void
+ onClose(): void
+
+} & Connect<typeof mapStateToProps, typeof mapDispatchToProps>;
+
+ //TODO: figure out why everything gets triggered twice...
+
+const ShowColumnsDialog: React.FunctionComponent<DialogProps> = (props) => {
+
+ const savedSettings = props.settingsName && props.settings.tables[props.settingsName];
+
+ const [checkedColumns, setCheckedColumns] = React.useState<{ property: string, display: boolean, title: string | undefined }[]>([]);
+
+ const open = Boolean(props.anchorEl);
+ const allColumnNames = props.columns.map(e => e.property);
+
+ React.useEffect(() => {
+
+ createHideShowSelection();
+
+ }, []);
+
+ React.useEffect(() => {
+
+ createHideShowSelection();
+
+ }, [props.settings.isInitialLoadDone]);
+
+
+ const createHideShowSelection = () => {
+ let columns = props.columns.map(e => { return { property: e.property, display: !Boolean(e.hide), title: e.title } });
+
+
+ if (savedSettings) {
+
+ if (columns.length !== savedSettings.columns.length) {
+ console.error("saved column length does not match current column length. Maybe a settings entry got wrongly overridden?")
+ }
+
+ //overwrite column data with settings
+ savedSettings?.columns.forEach(el => {
+ let foundIndex = columns.findIndex(e => e.property == el.property);
+ if (columns[foundIndex] !== undefined)
+ columns[foundIndex].display = el.displayed;
+ });
+
+ } else {
+ console.warn("No settingsName set, changes will not be saved.")
+ }
+
+ setCheckedColumns(columns);
+
+ const hideColumns = columns.filter(el => !el.display).map(e => e.property);
+ props.hideColumns(hideColumns);
+ }
+
+
+ const handleChange = (propertyName: string, checked: boolean) => {
+ if (!checked) {
+ props.hideColumns([propertyName]);
+ } else {
+ props.showColumns([propertyName])
+
+ }
+
+ let updatedList = checkedColumns.map(item => {
+ if (item.property == propertyName) {
+ return { ...item, display: checked };
+ }
+ return item;
+ });
+
+ setCheckedColumns(updatedList);
+ };
+
+ const onHideAll = () => {
+
+ switchCheckedColumns(false);
+ props.hideColumns(allColumnNames);
+ }
+
+ const onShowAll = () => {
+
+ switchCheckedColumns(true);
+ props.showColumns(allColumnNames);
+ }
+
+ const onClose = () => {
+
+ const tableColumns: TableSettingsColumn[] = checkedColumns.map(el => {
+ return {
+ property: el.property,
+ displayed: el.display
+ }
+ });
+
+ if (props.settingsName) {
+ props.saveToSettings(props.settingsName, tableColumns);
+ }
+ props.onClose();
+
+ }
+
+ const switchCheckedColumns = (changeToValue: boolean) => {
+ let updatedList = checkedColumns.map(item => {
+ return { ...item, display: changeToValue };
+ });
+
+ setCheckedColumns(updatedList);
+
+ }
+
+ return (<Popover open={open} onClose={onClose}
+ anchorEl={props.anchorEl}
+ anchorOrigin={{
+ vertical: 'top',
+ horizontal: 'left',
+ }} >
+ <div>
+ <Typography fontWeight={600} style={{ margin: 10 }} >Hide / Show Columns</Typography>
+ </div>
+ <div style={{ display: "flex", flexDirection: "column", margin: 10 }}>
+ {
+ checkedColumns?.map((el, i) => {
+
+ return <>
+
+ <FormControlLabel
+ value="end"
+ key={"hide-show-column-"+i}
+ aria-label={"hide-or-show-column-button"}
+ control={<Switch color="secondary" checked={el.display} onChange={e => handleChange(el.property, e.target.checked)} />}
+ label={el.title || el.property}
+ labelPlacement="end"
+ />
+ </>
+ })
+ }
+ <div style={{ display: "flex", flexDirection: "row", justifyContent: "space-between" }}>
+ <Button color="secondary" aria-label="hide-all-columns-button" onClick={(e) => onHideAll()}>Hide all</Button>
+ <Button color="secondary" aria-label="show-all-columns-button" onClick={(e) => onShowAll()}>Show all</Button>
+ </div>
+ </div>
+ </Popover>)
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(ShowColumnsDialog);
diff --git a/sdnr/wt-odlux/odlux/framework/src/components/material-table/tableFilter.tsx b/sdnr/wt-odlux/odlux/framework/src/components/material-table/tableFilter.tsx
new file mode 100644
index 000000000..1b9136844
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/components/material-table/tableFilter.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 { ColumnModel, ColumnType } from './columnModel';
+import { Theme } from '@mui/material/styles';
+
+
+import { WithStyles } from '@mui/styles';
+import withStyles from '@mui/styles/withStyles';
+import createStyles from '@mui/styles/createStyles';
+
+
+import TableCell from '@mui/material/TableCell';
+import TableRow from '@mui/material/TableRow';
+import Input from '@mui/material/Input';
+import { Select, FormControl, InputLabel, MenuItem, SelectChangeEvent } from '@mui/material';
+import { toAriaLabel } from '../../utilities/yangHelper';
+
+
+const styles = (theme: Theme) => createStyles({
+ container: {
+ display: 'flex',
+ flexWrap: 'wrap',
+ },
+ input: {
+ margin: theme.spacing(1),
+ },
+ numberInput: {
+ float: "right"
+ }
+});
+
+interface IEnhancedTableFilterComponentProps extends WithStyles<typeof styles> {
+ onFilterChanged: (property: string, filterTerm: string) => void;
+ filter: { [property: string]: string };
+ columns: ColumnModel<{}>[];
+ hiddenColumns: string[];
+ enableSelection?: boolean;
+}
+
+class EnhancedTableFilterComponent extends React.Component<IEnhancedTableFilterComponentProps> {
+ createSelectFilterHandler = (property: string) => (event: SelectChangeEvent<HTMLSelectElement | string>) => {
+ this.props.onFilterChanged && this.props.onFilterChanged(property, event.target.value as string);
+ };
+ createInputFilterHandler = (property: string) => (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
+ this.props.onFilterChanged && this.props.onFilterChanged(property, event.currentTarget.value);
+ };
+
+
+ render() {
+ const { columns, filter, classes } = this.props;
+ return (
+ <TableRow>
+ {this.props.enableSelection
+ ? <TableCell padding="checkbox" style={{ width: "50px" }}>
+ </TableCell>
+ : null
+ }
+ {columns.map((col, ind) => {
+ const style = col.width ? { width: col.width } : {};
+ const tableCell = (
+ <TableCell
+ className={col.type === ColumnType.numeric ? classes.numberInput : ''}
+ key={col.property}
+ padding={col.disablePadding ? 'none' : 'normal'}
+ style={style}
+ >
+ {col.disableFilter || (col.type === ColumnType.custom)
+ ? null
+ : (col.type === ColumnType.boolean)
+ ? <Select variant="standard" className={classes.input} aria-label={col.title ? toAriaLabel(col.title as string) + '-filter' : `${ind + 1}-filter`}
+ value={filter[col.property] !== undefined ? filter[col.property] : ''}
+ onChange={this.createSelectFilterHandler(col.property)}
+ inputProps={{ name: `${col.property}-bool`, id: `${col.property}-bool` }} >
+ <MenuItem value={undefined} aria-label="none-value" >
+ <em>None</em>
+ </MenuItem>
+ <MenuItem aria-label="true-value" value={true as any as string}>{col.labels ? col.labels["true"] : "true"}</MenuItem>
+ <MenuItem aria-label="false-value" value={false as any as string}>{col.labels ? col.labels["false"] : "false"}</MenuItem>
+ </Select>
+ : <Input className={classes.input}
+ inputProps={{ 'aria-label': col.title ? toAriaLabel(col.title as string) + '-filter' : `${ind + 1}-filter` }}
+ value={filter[col.property] || ''}
+ onChange={this.createInputFilterHandler(col.property)} />}
+ </TableCell>
+ );
+
+ const showColumn = !this.props.hiddenColumns.includes(col.property);
+
+ return showColumn && tableCell;
+ }, this)}
+ </TableRow>
+ );
+ }
+}
+
+export const EnhancedTableFilter = withStyles(styles)(EnhancedTableFilterComponent); \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/components/material-table/tableHead.tsx b/sdnr/wt-odlux/odlux/framework/src/components/material-table/tableHead.tsx
new file mode 100644
index 000000000..d6f7b7def
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/components/material-table/tableHead.tsx
@@ -0,0 +1,127 @@
+/**
+ * ============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 { ColumnModel, ColumnType } from './columnModel';
+import { Theme } from '@mui/material/styles';
+
+import { WithStyles } from '@mui/styles';
+import withStyles from '@mui/styles/withStyles';
+import createStyles from '@mui/styles/createStyles';
+
+import TableSortLabel from '@mui/material/TableSortLabel';
+import TableCell from '@mui/material/TableCell';
+import TableHead from '@mui/material/TableHead';
+import TableRow from '@mui/material/TableRow';
+import Checkbox from '@mui/material/Checkbox';
+import Tooltip from '@mui/material/Tooltip';
+
+const styles = (theme: Theme) => createStyles({
+ header: {
+ backgroundColor: "#fafafa",
+ position: "sticky",
+ top: 0
+ }
+});
+
+
+type styles_header = WithStyles<typeof styles>;
+
+interface IEnhancedTableHeadComponentProps extends styles_header {
+ numSelected: number | null;
+ onRequestSort: (event: React.SyntheticEvent, property: string) => void;
+ onSelectAllClick: () => void;
+ order: 'asc' | 'desc';
+ orderBy: string | null;
+ rowCount: number;
+ columns: ColumnModel<{}>[];
+ hiddenColumns: string[];
+ enableSelection?: boolean;
+ allowHtmlHeader?: boolean;
+}
+
+class EnhancedTableHeadComponent extends React.Component<IEnhancedTableHeadComponentProps> {
+ createSortHandler = (property: string) => (event: React.SyntheticEvent) => {
+ this.props.onRequestSort(event, property);
+ };
+
+ render() {
+ const { onSelectAllClick, order, orderBy, numSelected, rowCount, columns } = this.props;
+ const {classes} = this.props;
+
+ return (
+ <TableHead>
+ <TableRow>
+ { this.props.enableSelection
+ ? <TableCell padding="checkbox" style={ { width: "50px" } } className= {classes.header} >
+ <Checkbox
+ indeterminate={ numSelected && numSelected > 0 && numSelected < rowCount || undefined }
+ checked={ numSelected === rowCount }
+ onChange={ onSelectAllClick }
+ />
+ </TableCell>
+ : null
+ }
+ { columns.map(col => {
+ const style = col.width ? { width: col.width } : {};
+ const tableCell = (
+ <TableCell className= {classes.header}
+ key={ col.property }
+ align={ col.type === ColumnType.numeric ? 'right' : 'left' }
+ padding={ col.disablePadding ? 'none' : 'normal' }
+ sortDirection={ orderBy === (col.property) ? order : false }
+ style={ style }
+ >
+ { col.disableSorting || (col.type === ColumnType.custom)
+ ? <TableSortLabel
+ active={ false }
+ direction={ undefined }
+ >
+ { col.title || col.property }
+ </TableSortLabel>
+ : <Tooltip disableInteractive
+ title="Sort"
+ placement={ col.type === ColumnType.numeric ? 'bottom-end' : 'bottom-start' }
+ enterDelay={ 300 }
+ >
+ <TableSortLabel
+ active={ orderBy === col.property }
+ direction={ order || undefined }
+ onClick={ this.createSortHandler(col.property) }
+ >
+ {
+ this.props.allowHtmlHeader ? <div className="content" dangerouslySetInnerHTML={{__html: col.title || col.property}}></div>
+ : (col.title || col.property )
+ }
+ </TableSortLabel>
+ </Tooltip> }
+ </TableCell>
+ );
+
+ //show column if...
+ const showColumn = !this.props.hiddenColumns.includes(col.property);
+
+ return showColumn && tableCell;
+ }, this) }
+ </TableRow>
+ </TableHead>
+ );
+ }
+}
+
+export const EnhancedTableHead = withStyles(styles)(EnhancedTableHeadComponent); \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/components/material-table/tableToolbar.tsx b/sdnr/wt-odlux/odlux/framework/src/components/material-table/tableToolbar.tsx
new file mode 100644
index 000000000..143b802a4
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/components/material-table/tableToolbar.tsx
@@ -0,0 +1,191 @@
+/**
+ * ============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, lighten } from '@mui/material/styles';
+
+import { WithStyles } from '@mui/styles';
+import withStyles from '@mui/styles/withStyles';
+import createStyles from '@mui/styles/createStyles';
+
+import IconButton from '@mui/material/IconButton';
+import Tooltip from '@mui/material/Tooltip';
+import Toolbar from '@mui/material/Toolbar';
+import Typography from '@mui/material/Typography';
+import DeleteIcon from '@mui/icons-material/Delete';
+import MoreIcon from '@mui/icons-material/MoreVert';
+import FilterListIcon from '@mui/icons-material/FilterList';
+import MenuItem from '@mui/material/MenuItem';
+import Menu from '@mui/material/Menu';
+import { SvgIconProps } from '@mui/material/SvgIcon';
+import { Button } from '@mui/material';
+import { ColumnModel } from './columnModel';
+import ShowColumnsDialog from './showColumnDialog'
+
+const styles = (theme: Theme) => createStyles({
+ root: {
+ paddingRight: theme.spacing(1),
+ },
+ highlight:
+ theme.palette.mode === 'light'
+ ? {
+ color: theme.palette.secondary.main,
+ backgroundColor: lighten(theme.palette.secondary.light, 0.85),
+ }
+ : {
+ color: theme.palette.text.primary,
+ backgroundColor: theme.palette.secondary.dark,
+ },
+ spacer: {
+ flex: '1 1 100%',
+ },
+ actions: {
+ color: theme.palette.text.secondary,
+ display: "flex",
+ flex: "auto",
+ flexDirection: "row"
+ },
+ title: {
+ flex: '0 0 auto',
+ },
+ menuButton: {
+ marginLeft: -12,
+ marginRight: 20,
+ },
+});
+
+interface ITableToolbarComponentProps extends WithStyles<typeof styles> {
+ numSelected: number | null;
+ title?: string;
+ tableId: string | null;
+ customActionButtons?: { icon: React.ComponentType<SvgIconProps>, tooltip?: string, ariaLabel: string, onClick: () => void, disabled?: boolean }[];
+ columns: ColumnModel<{}>[];
+ onHideColumns: (columnNames: string[]) => void
+ onShowColumns: (columnNames: string[]) => void
+ onToggleFilter: () => void;
+ onExportToCsv: () => void;
+}
+
+class TableToolbarComponent extends React.Component<ITableToolbarComponentProps, { anchorEl: EventTarget & HTMLElement | null, anchorElDialog: HTMLElement | null }> {
+
+
+ constructor(props: ITableToolbarComponentProps) {
+ super(props);
+
+ this.state = {
+ anchorEl: null,
+ anchorElDialog: null
+ };
+ }
+
+ private handleMenu = (event: React.MouseEvent<HTMLElement>) => {
+ this.setState({ anchorEl: event.currentTarget });
+ };
+
+ private handleClose = () => {
+ this.setState({ anchorEl: null });
+ };
+
+ private showColumnsDialog = (event: React.MouseEvent<HTMLElement>) =>{
+ this.setState({ anchorElDialog: this.state.anchorEl });
+ }
+
+ private onCloseDialog = () =>{
+ this.setState({ anchorElDialog: null });
+
+ }
+
+ render() {
+ const { numSelected, classes } = this.props;
+ const open = !!this.state.anchorEl;
+ const buttonPrefix = this.props.tableId !== null ? this.props.tableId : 'table';
+ return (
+ <>
+ <Toolbar className={`${classes.root} ${numSelected && numSelected > 0 ? classes.highlight : ''} `} >
+ <div className={classes.title}>
+ {numSelected && numSelected > 0 ? (
+ <Typography color="inherit" variant="subtitle1">
+ {numSelected} selected
+ </Typography>
+ ) : (
+ <Typography variant="h5" id="tableTitle">
+ {this.props.title || null}
+ </Typography>
+ )}
+ </div>
+ <div className={classes.spacer} />
+ <div className={classes.actions}>
+ {this.props.customActionButtons
+ ? this.props.customActionButtons.map((action, ind) => (
+ <Tooltip disableInteractive key={`custom-action-${ind}`} title={action.tooltip || ''}>
+ <IconButton
+ disabled={action.disabled}
+ aria-label={`${buttonPrefix}-${action.ariaLabel}-button`}
+ onClick={() => action.onClick()}
+ size="large">
+ <action.icon />
+ </IconButton>
+ </Tooltip>
+ ))
+ : null}
+ {numSelected && numSelected > 0 ? (
+ <Tooltip disableInteractive title="Delete">
+ <IconButton aria-label={`${buttonPrefix}-delete-button`} size="large">
+ <DeleteIcon />
+ </IconButton>
+ </Tooltip>
+ ) : (
+ <Tooltip disableInteractive title="Filter list">
+ <IconButton
+ aria-label={`${buttonPrefix}-filter-list-button`}
+ onClick={() => { this.props.onToggleFilter && this.props.onToggleFilter() }}
+ size="large">
+ <FilterListIcon />
+ </IconButton>
+ </Tooltip>
+ )}
+ <Tooltip disableInteractive title="Actions">
+ <IconButton
+ color="inherit"
+ aria-label={`${buttonPrefix}-additional-actions-button`}
+ aria-owns={open ? 'menu-appbar' : undefined}
+ aria-haspopup="true"
+ onClick={this.handleMenu}
+ size="large">
+ <MoreIcon />
+ </IconButton>
+ </Tooltip>
+ <Menu id="menu-appbar" anchorEl={this.state.anchorEl} anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
+ transformOrigin={{ vertical: 'top', horizontal: 'right' }} open={open} onClose={this.handleClose} >
+ <MenuItem aria-label="export-table-as-csv" onClick={(e) =>{ this.props.onExportToCsv(); this.handleClose()}}>Export as CSV</MenuItem>
+ <MenuItem aria-label="hide-show-table-columns" onClick={(e) =>{ this.showColumnsDialog(e); this.handleClose()}}>Hide/show columns</MenuItem>
+ </Menu>
+ </div>
+ </Toolbar>
+ <ShowColumnsDialog
+ anchorEl={this.state.anchorElDialog}
+ onClose={this.onCloseDialog}
+ settingsName={this.props.tableId}
+ columns={this.props.columns}
+ hideColumns={this.props.onHideColumns}
+ showColumns={this.props.onShowColumns} />
+ </>
+ );
+ }
+}
+
+export const TableToolbar = withStyles(styles)(TableToolbarComponent); \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/components/material-table/utilities.ts b/sdnr/wt-odlux/odlux/framework/src/components/material-table/utilities.ts
new file mode 100644
index 000000000..e2fda7647
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/components/material-table/utilities.ts
@@ -0,0 +1,357 @@
+/**
+ * ============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, IActionHandler } from '../../flux/action';
+import { Dispatch } from '../../flux/store';
+
+import { AddErrorInfoAction } from '../../actions/errorActions';
+import { IApplicationStoreState } from '../../store/applicationStore';
+
+export const RowDisabled = Symbol("RowDisabled");
+import { DataCallback } from ".";
+
+export interface IExternalTableState<TData> {
+ order: 'asc' | 'desc';
+ orderBy: string | null;
+ selected: any[] | null;
+ hiddenColumns: string[]
+ rows: (TData & { [RowDisabled]?: boolean })[];
+ total: number;
+ page: number;
+ rowsPerPage: number;
+ loading: boolean;
+ showFilter: boolean;
+ filter: { [property: string]: string };
+ preFilter: { [property: string]: string };
+}
+
+export type ExternalMethodes<TData> = {
+ reloadAction: (dispatch: Dispatch, getAppState: () => IApplicationStoreState) => Promise<void | AddErrorInfoAction>;
+ createActions: (dispatch: Dispatch, skipRefresh?: boolean) => {
+ onRefresh: () => void;
+ onHandleRequestSort: (orderBy: string) => void;
+ onHandleExplicitRequestSort: (property: string, sortOrder: "asc" | "desc") => void;
+ onToggleFilter: (refresh?: boolean | undefined) => void;
+ onFilterChanged: (property: string, filterTerm: string) => void;
+ onHandleChangePage: (page: number) => void;
+ onHandleChangeRowsPerPage: (rowsPerPage: number | null) => void;
+ onHideColumns: (columnName: string[]) => void;
+ onShowColumns: (columnName: string[]) => void;
+ onClearFilters: () => void;
+ },
+ createPreActions: (dispatch: Dispatch, skipRefresh?: boolean) => {
+ onPreFilterChanged: (preFilter: {
+ [key: string]: string;
+ }) => void;
+ };
+ createProperties: (state: IApplicationStoreState) => IExternalTableState<TData>;
+ actionHandler: IActionHandler<IExternalTableState<TData>, Action>;
+}
+
+
+/** Create an actionHandler and actions for external table states. */
+export function createExternal<TData>(callback: DataCallback<TData>, selectState: (appState: IApplicationStoreState) => IExternalTableState<TData>) : ExternalMethodes<TData> ;
+export function createExternal<TData>(callback: DataCallback<TData>, selectState: (appState: IApplicationStoreState) => IExternalTableState<TData>, disableRow: (data: TData) => boolean) : ExternalMethodes<TData>;
+export function createExternal<TData>(callback: DataCallback<TData>, selectState: (appState: IApplicationStoreState) => IExternalTableState<TData>, disableRow?: (data: TData) => boolean) : ExternalMethodes<TData> {
+
+ //#region Actions
+ abstract class TableAction extends Action { }
+
+
+ class RequestSortAction extends TableAction {
+ constructor(public orderBy: string) {
+ super();
+ }
+ }
+
+ class RequestExplicitSortAction extends TableAction {
+ constructor(public propertyName: string, public sortOrder: "asc" | "desc") {
+ super();
+ }
+ }
+
+ class SetSelectedAction extends TableAction {
+ constructor(public selected: TData[] | null) {
+ super();
+ }
+ }
+
+ class SetPageAction extends TableAction {
+ constructor(public page: number) {
+ super();
+ }
+ }
+
+ class SetRowsPerPageAction extends TableAction {
+ constructor(public rowsPerPage: number) {
+ super();
+ }
+ }
+
+ class SetPreFilterChangedAction extends TableAction {
+ constructor(public preFilter: { [key: string]: string }) {
+ super();
+ }
+ }
+
+ class SetFilterChangedAction extends TableAction {
+ constructor(public filter: { [key: string]: string }) {
+ super();
+ }
+ }
+
+ class SetShowFilterAction extends TableAction {
+ constructor(public show: boolean) {
+ super();
+ }
+ }
+
+ class RefreshAction extends TableAction {
+ constructor() {
+ super();
+ }
+ }
+
+ class SetResultAction extends TableAction {
+ constructor(public result: { page: number, total: number, rows: TData[] }) {
+ super();
+ }
+ }
+
+ class HideColumnsAction extends TableAction{
+ constructor(public property: string[]){
+ super();
+ }
+ }
+
+ class ShowColumnsAction extends TableAction{
+ constructor(public property: string[]){
+ super();
+ }
+ }
+
+ // #endregion
+
+ //#region Action Handler
+ const externalTableStateInit: IExternalTableState<TData> = {
+ order: 'asc',
+ orderBy: null,
+ selected: null,
+ hiddenColumns:[],
+ rows: [],
+ total: 0,
+ page: 0,
+ rowsPerPage: 10,
+ loading: false,
+ showFilter: false,
+ filter: {},
+ preFilter: {}
+ };
+
+ const externalTableStateActionHandler: IActionHandler<IExternalTableState<TData>> = (state = externalTableStateInit, action) => {
+ if (!(action instanceof TableAction)) return state;
+ if (action instanceof RefreshAction) {
+ state = {
+ ...state,
+ loading: true
+ }
+ } else if (action instanceof SetResultAction) {
+ state = {
+ ...state,
+ loading: false,
+ rows: disableRow
+ ? action.result.rows.map((row: TData) => ({...row, [RowDisabled]: disableRow(row) }))
+ : action.result.rows,
+ total: action.result.total,
+ page: action.result.page,
+ }
+ } else if (action instanceof RequestSortAction) {
+ state = {
+ ...state,
+ loading: true,
+ orderBy: state.orderBy === action.orderBy && state.order === 'desc' ? null : action.orderBy,
+ order: state.orderBy === action.orderBy && state.order === 'asc' ? 'desc' : 'asc',
+ }
+ } else if (action instanceof RequestExplicitSortAction) {
+ state = {
+ ...state,
+ loading: true,
+ orderBy: action.propertyName,
+ order: action.sortOrder
+ }
+ }
+ else if (action instanceof SetShowFilterAction) {
+ state = {
+ ...state,
+ loading: true,
+ showFilter: action.show
+ }
+ } else if (action instanceof SetPreFilterChangedAction) {
+ state = {
+ ...state,
+ loading: true,
+ preFilter: action.preFilter
+ }
+ } else if (action instanceof SetFilterChangedAction) {
+ state = {
+ ...state,
+ loading: true,
+ filter: action.filter
+ }
+ } else if (action instanceof SetPageAction) {
+ state = {
+ ...state,
+ loading: true,
+ page: action.page
+ }
+ } else if (action instanceof SetRowsPerPageAction) {
+ state = {
+ ...state,
+ loading: true,
+ rowsPerPage: action.rowsPerPage
+ }
+ }
+ else if (action instanceof HideColumnsAction){
+
+ //merge arrays, remove duplicates
+ const newArray = [...new Set([...state.hiddenColumns, ...action.property])]
+ state = {...state, hiddenColumns: newArray};
+ }
+ else if(action instanceof ShowColumnsAction){
+
+ const newArray = state.hiddenColumns.filter(el=> !action.property.includes(el));
+ state = {...state, hiddenColumns: newArray};
+ }
+
+ return state;
+ }
+
+ //const createTableAction(tableAction)
+
+ //#endregion
+ const reloadAction = (dispatch: Dispatch, getAppState: () => IApplicationStoreState) => {
+ dispatch(new RefreshAction());
+ const ownState = selectState(getAppState());
+ const filter = { ...ownState.preFilter, ...(ownState.showFilter && ownState.filter || {}) };
+ return Promise.resolve(callback(ownState.page, ownState.rowsPerPage, ownState.orderBy, ownState.order, filter)).then(result => {
+
+ if (ownState.page > 0 && ownState.rowsPerPage * ownState.page > result.total) { //if result is smaller than the currently shown page, new search and repaginate
+
+ let newPage = Math.floor(result.total / ownState.rowsPerPage);
+
+ Promise.resolve(callback(newPage, ownState.rowsPerPage, ownState.orderBy, ownState.order, filter)).then(result1 => {
+ dispatch(new SetResultAction(result1));
+ });
+
+
+ } else {
+ dispatch(new SetResultAction(result));
+ }
+
+
+ }).catch(error => dispatch(new AddErrorInfoAction(error)));
+ };
+
+ const createPreActions = (dispatch: Dispatch, skipRefresh: boolean = false) => {
+ return {
+ onPreFilterChanged: (preFilter: { [key: string]: string }) => {
+ dispatch(new SetPreFilterChangedAction(preFilter));
+ (!skipRefresh) && dispatch(reloadAction);
+ }
+ };
+ }
+
+ const createActions = (dispatch: Dispatch, skipRefresh: boolean = false) => {
+ return {
+ onRefresh: () => {
+ dispatch(reloadAction);
+ },
+ onHandleRequestSort: (orderBy: string) => {
+ dispatch((dispatch: Dispatch) => {
+ dispatch(new RequestSortAction(orderBy));
+ (!skipRefresh) && dispatch(reloadAction);
+ });
+ },
+ onHandleExplicitRequestSort: (property: string, sortOrder: "asc" | "desc") => {
+ dispatch((dispatch: Dispatch) => {
+ dispatch(new RequestExplicitSortAction(property, sortOrder));
+ (!skipRefresh) && dispatch(reloadAction);
+ });
+ },
+ onToggleFilter: (refresh?: boolean) => {
+ dispatch((dispatch: Dispatch, getAppState: () => IApplicationStoreState) => {
+ const { showFilter } = selectState(getAppState());
+ dispatch(new SetShowFilterAction(!showFilter));
+ if (!skipRefresh && (refresh === undefined || refresh))
+ dispatch(reloadAction);
+ });
+ },
+ onFilterChanged: (property: string, filterTerm: string) => {
+ dispatch((dispatch: Dispatch, getAppState: () => IApplicationStoreState) => {
+ let { filter } = selectState(getAppState());
+ filter = { ...filter, [property]: filterTerm };
+ dispatch(new SetFilterChangedAction(filter));
+ (!skipRefresh) && dispatch(reloadAction);
+ });
+ },
+ onHandleChangePage: (page: number) => {
+ dispatch((dispatch: Dispatch) => {
+ dispatch(new SetPageAction(page));
+ (!skipRefresh) && dispatch(reloadAction);
+ });
+ },
+ onHandleChangeRowsPerPage: (rowsPerPage: number | null) => {
+ dispatch((dispatch: Dispatch) => {
+ dispatch(new SetRowsPerPageAction(rowsPerPage || 10));
+ (!skipRefresh) && dispatch(reloadAction);
+ });
+ },
+ onHideColumns: (columnName: string[]) =>{
+ dispatch((dispatch: Dispatch) => {
+ dispatch(new HideColumnsAction(columnName));
+ })
+ },
+ onShowColumns: (columnName: string[]) =>{
+ dispatch((dispatch: Dispatch) => {
+ dispatch(new ShowColumnsAction(columnName));
+ })
+ },
+ onClearFilters: () => {
+ dispatch((dispatch: Dispatch) => {
+ let filter = { };
+ dispatch(new SetFilterChangedAction(filter));
+ });
+ },
+ // selected:
+ };
+ };
+
+ const createProperties = (state: IApplicationStoreState) => {
+ return {
+ ...selectState(state)
+ }
+ }
+
+ return {
+ reloadAction: reloadAction,
+ createActions: createActions,
+ createProperties: createProperties,
+ createPreActions: createPreActions,
+ actionHandler: externalTableStateActionHandler,
+ }
+}
+
diff --git a/sdnr/wt-odlux/odlux/framework/src/components/material-ui/index.ts b/sdnr/wt-odlux/odlux/framework/src/components/material-ui/index.ts
new file mode 100644
index 000000000..096e4439d
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/components/material-ui/index.ts
@@ -0,0 +1,22 @@
+/**
+ * ============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 { ListItemLink } from './listItemLink';
+export { Panel } from './panel';
+export { ToggleButton, ToggleButtonClassKey } from './toggleButton';
+export { TreeView, TreeItem, TreeViewCtorType} from './treeView';
+export { Loader } from './loader';
diff --git a/sdnr/wt-odlux/odlux/framework/src/components/material-ui/listItemLink.tsx b/sdnr/wt-odlux/odlux/framework/src/components/material-ui/listItemLink.tsx
new file mode 100644
index 000000000..626cb8978
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/components/material-ui/listItemLink.tsx
@@ -0,0 +1,83 @@
+/**
+ * ============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 { NavLink, Link, Route } from 'react-router-dom';
+
+import ListItem from '@mui/material/ListItem';
+import ListItemIcon from '@mui/material/ListItemIcon';
+import ListItemText from '@mui/material/ListItemText';
+
+import { Theme } from '@mui/material/styles';
+import { WithStyles } from '@mui/styles';
+import withStyles from '@mui/styles/withStyles';
+import createStyles from '@mui/styles/createStyles';
+import { toAriaLabel } from '../../utilities/yangHelper';
+import { IconType } from '../../models/iconDefinition';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+
+const styles = (theme: Theme) => createStyles({
+ active: {
+ backgroundColor: theme.palette.action.selected
+ }
+});
+
+export interface IListItemLinkProps extends WithStyles<typeof styles> {
+ icon: IconType | null;
+ primary: string | React.ComponentType;
+ secondary?: React.ComponentType;
+ to: string;
+ exact?: boolean;
+ external?: boolean;
+}
+
+export const ListItemLink = withStyles(styles)((props: IListItemLinkProps) => {
+ const { icon, primary: Primary, secondary: Secondary, classes, to, exact = false, external=false } = props;
+ const renderLink = (itemProps: any): JSX.Element => (
+ props.external ? <a target="_blank" href={to} { ...itemProps }></a> :
+ <NavLink exact={ exact } to={ to } activeClassName={ classes.active } { ...itemProps } />);
+
+ const customIconHeight = 22;
+ const ariaLabel = typeof Primary === 'string' ? toAriaLabel("link-to-"+Primary) : toAriaLabel("link-to-"+Primary.displayName);
+
+ //create menu icon, either using an faIcon or a link to a custom svg icon
+ //moved to one place for easier usage
+ const listItemIcon = icon && ( typeof icon === 'string' ? <img height={customIconHeight} src={icon} /> : <FontAwesomeIcon icon={icon} /> );
+
+ return (
+ <>
+ <ListItem button component={ renderLink } aria-label={ariaLabel}>
+ { icon
+ ? <ListItemIcon>{ listItemIcon }</ListItemIcon>
+ : null
+ }
+ { typeof Primary === 'string'
+ ? <ListItemText primary={ Primary } style={{ padding: 0 }} />
+ : <Primary />
+ }
+ </ListItem>
+ { Secondary
+ ? <Route exact={ exact } path={ to } component={ Secondary } />
+ : null
+ }
+ </>
+ );
+ }
+);
+
+export default ListItemLink;
+
diff --git a/sdnr/wt-odlux/odlux/framework/src/components/material-ui/loader.tsx b/sdnr/wt-odlux/odlux/framework/src/components/material-ui/loader.tsx
new file mode 100644
index 000000000..bd523e1f4
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/components/material-ui/loader.tsx
@@ -0,0 +1,49 @@
+/**
+ * ============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 } from '@mui/material/styles';
+
+import { WithStyles } from '@mui/styles';
+import withStyles from '@mui/styles/withStyles';
+import createStyles from '@mui/styles/createStyles';
+
+const styles = (theme: Theme) => createStyles({
+ "@keyframes spin": {
+ "0%": { transform: "rotate(0deg)" },
+ "100%": { transform: "rotate(360deg)" },
+ },
+ loader: {
+ border: `16px solid ${theme.palette.grey.A200}`,
+ borderTop: `16px solid ${theme.palette.secondary.main}`,
+ borderRadius: "50%",
+ width: "120px",
+ height: "120px",
+ animation: "$spin 2s linear infinite",
+ }
+});
+
+const LoaderComponent: React.FC<WithStyles<typeof styles>> = (props) => {
+ return (
+ <div className={props.classes.loader} />
+ );
+};
+
+export const Loader = withStyles(styles)(LoaderComponent);
diff --git a/sdnr/wt-odlux/odlux/framework/src/components/material-ui/panel.tsx b/sdnr/wt-odlux/odlux/framework/src/components/material-ui/panel.tsx
new file mode 100644
index 000000000..6d192d2f0
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/components/material-ui/panel.tsx
@@ -0,0 +1,76 @@
+/**
+ * ============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 } from '@mui/material/styles';
+
+import { WithStyles } from '@mui/styles';
+import withStyles from '@mui/styles/withStyles';
+import createStyles from '@mui/styles/createStyles';
+
+import { Accordion, AccordionSummary, AccordionDetails, Typography, AccordionActions } from '@mui/material';
+
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
+import { SvgIconProps } from '@mui/material/SvgIcon';
+
+const styles = (theme: Theme) => createStyles({
+ accordion: {
+ // background: theme.palette.secondary.dark,
+ // color: theme.palette.primary.contrastText
+ },
+ detail: {
+ // background: theme.palette.background.paper,
+ // color: theme.palette.text.primary,
+ position: "relative",
+ flexDirection: 'column'
+ },
+ text: {
+ // color: theme.palette.common.white,
+ // fontSize: "1rem"
+ },
+});
+
+type PanalProps = WithStyles<typeof styles> & {
+ activePanel: string | null,
+ panelId: string,
+ title: string,
+ customActionButtons?: JSX.Element[];
+ onToggle: (panelId: string | null) => void;
+}
+
+const PanelComponent: React.SFC<PanalProps> = (props) => {
+ const { classes, activePanel, onToggle } = props;
+ return (
+ <Accordion className={classes.accordion} expanded={activePanel === props.panelId} onChange={() => onToggle(props.panelId)} >
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography className={classes.text} >{props.title}</Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.detail}>
+ {props.children}
+ </AccordionDetails>
+ {props.customActionButtons
+ ? <AccordionActions>
+ {props.customActionButtons}
+ </AccordionActions>
+ : null}
+ </Accordion>
+ );
+};
+
+export const Panel = withStyles(styles)(PanelComponent);
+export default Panel; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/components/material-ui/snackDisplay.tsx b/sdnr/wt-odlux/odlux/framework/src/components/material-ui/snackDisplay.tsx
new file mode 100644
index 000000000..437784ce5
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/components/material-ui/snackDisplay.tsx
@@ -0,0 +1,74 @@
+/**
+ * ============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 { IApplicationStoreState } from '../../store/applicationStore';
+import { Connect, connect, IDispatcher } from '../../flux/connect';
+import { RemoveSnackbarNotification } from '../../actions/snackbarActions';
+
+import { WithSnackbarProps, withSnackbar } from 'notistack';
+
+const mapProps = (state: IApplicationStoreState) => ({
+ notifications: state.framework.applicationState.snackBars
+});
+
+const mapDispatch = (dispatcher: IDispatcher) => ({
+ removeSnackbar: (key: number) => {
+ dispatcher.dispatch(new RemoveSnackbarNotification(key));
+ }
+});
+
+type DisplaySnackbarsComponentProps = Connect<typeof mapProps, typeof mapDispatch> & WithSnackbarProps;
+
+class DisplaySnackbarsComponent extends React.Component<DisplaySnackbarsComponentProps> {
+ private displayed: number[] = [];
+
+ private storeDisplayed = (id: number) => {
+ this.displayed = [...this.displayed, id];
+ };
+
+ public shouldComponentUpdate({ notifications: newSnacks = [] }: DisplaySnackbarsComponentProps) {
+
+ const { notifications: currentSnacks } = this.props;
+ let notExists = false;
+ for (let i = 0; i < newSnacks.length; i++) {
+ if (notExists) continue;
+ notExists = notExists || !currentSnacks.filter(({ key }) => newSnacks[i].key === key).length;
+ }
+ return notExists;
+ }
+
+ componentDidUpdate() {
+ const { notifications = [] } = this.props;
+
+ notifications.forEach(notification => {
+ if (this.displayed.includes(notification.key)) return;
+ const options = notification.options || {};
+ this.props.enqueueSnackbar(notification.message, options);
+ this.storeDisplayed(notification.key);
+ this.props.removeSnackbar(notification.key);
+ });
+ }
+
+ render() {
+ return null;
+ }
+}
+
+const DisplayStackbars = withSnackbar(connect(mapProps, mapDispatch)(DisplaySnackbarsComponent));
+export default DisplayStackbars; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/components/material-ui/toggleButton.tsx b/sdnr/wt-odlux/odlux/framework/src/components/material-ui/toggleButton.tsx
new file mode 100644
index 000000000..54f14a7e0
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/components/material-ui/toggleButton.tsx
@@ -0,0 +1,181 @@
+/**
+ * ============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 classNames from 'classnames';
+import { Theme, alpha } from '@mui/material/styles';
+import { WithStyles } from '@mui/styles';
+import withStyles from '@mui/styles/withStyles';
+import createStyles from '@mui/styles/createStyles';
+import ButtonBase from '@mui/material/ButtonBase';
+
+
+export const styles = (theme: Theme) => createStyles({
+ /* Styles applied to the root element. */
+ root: {
+ ...theme.typography.button,
+ height: 32,
+ minWidth: 48,
+ margin: 0,
+ padding: `${theme.spacing(1 - 4)} ${theme.spacing(1.5)}`,
+ borderRadius: 2,
+ willChange: 'opacity',
+ color: alpha(theme.palette.action.active, 0.38),
+ '&:hover': {
+ textDecoration: 'none',
+ // Reset on mouse devices
+ backgroundColor: alpha(theme.palette.text.primary, 0.12),
+ '@media (hover: none)': {
+ backgroundColor: 'transparent',
+ },
+ '&.Mui-disabled': {
+ backgroundColor: 'transparent',
+ },
+ },
+ '&:not(:first-child)': {
+ borderTopLeftRadius: 0,
+ borderBottomLeftRadius: 0,
+ },
+ '&:not(:last-child)': {
+ borderTopRightRadius: 0,
+ borderBottomRightRadius: 0,
+ },
+ },
+ /* Styles applied to the root element if `disabled={true}`. */
+ disabled: {
+ color: alpha(theme.palette.action.disabled, 0.12),
+ },
+ /* Styles applied to the root element if `selected={true}`. */
+ selected: {
+ color: theme.palette.action.active,
+ '&:after': {
+ content: '""',
+ display: 'block',
+ position: 'absolute',
+ overflow: 'hidden',
+ borderRadius: 'inherit',
+ width: '100%',
+ height: '100%',
+ left: 0,
+ top: 0,
+ pointerEvents: 'none',
+ zIndex: 0,
+ backgroundColor: 'currentColor',
+ opacity: 0.38,
+ },
+ '& + &:before': {
+ content: '""',
+ display: 'block',
+ position: 'absolute',
+ overflow: 'hidden',
+ width: 1,
+ height: '100%',
+ left: 0,
+ top: 0,
+ pointerEvents: 'none',
+ zIndex: 0,
+ backgroundColor: 'currentColor',
+ opacity: 0.12,
+ },
+ },
+ /* Styles applied to the `label` wrapper element. */
+ label: {
+ width: '100%',
+ display: 'inherit',
+ alignItems: 'inherit',
+ justifyContent: 'inherit',
+ },
+});
+
+export type ToggleButtonClassKey = 'disabled' | 'root' | 'label' | 'selected';
+
+interface IToggleButtonProps extends WithStyles<typeof styles> {
+ className?: string;
+ component?: React.ReactType<IToggleButtonProps>;
+ disabled?: boolean;
+ disableFocusRipple?: boolean;
+ disableRipple?: boolean;
+ selected?: boolean;
+ type?: string;
+ value?: any;
+ onClick?: (event: React.FormEvent<HTMLElement>, value?: any) => void;
+ onChange?: (event: React.FormEvent<HTMLElement>, value?: any) => void;
+}
+
+class ToggleButtonComponent extends React.Component<IToggleButtonProps> {
+ handleChange = (event: React.FormEvent<HTMLElement>) => {
+ const { onChange, onClick, value } = this.props;
+
+ event.stopPropagation();
+ if (onClick) {
+ onClick(event, value);
+ if (event.isDefaultPrevented()) {
+ return;
+ }
+ }
+
+ if (onChange) {
+ onChange(event, value);
+ }
+ event.preventDefault();
+ };
+
+ render() {
+ const {
+ children,
+ className: classNameProp,
+ classes,
+ disableFocusRipple,
+ disabled,
+ selected,
+ ...other
+ } = this.props;
+
+ const className = classNames(
+ classes.root,
+ {
+ [classes.disabled]: disabled,
+ [classes.selected]: selected,
+ },
+ classNameProp,
+ );
+
+ return (
+ <ButtonBase
+ className={className}
+ disabled={disabled}
+ focusRipple={!disableFocusRipple}
+ onClick={this.handleChange}
+ href="#"
+ {...other}
+ >
+ <span className={classes.label}>{children}</span>
+ </ButtonBase>
+ );
+ }
+ public static defaultProps = {
+ disabled: false,
+ disableFocusRipple: false,
+ disableRipple: false,
+ };
+
+ public static muiName = 'ToggleButton';
+}
+
+export const ToggleButton = withStyles(styles, { name: 'MuiToggleButton' })(ToggleButtonComponent);
+export default ToggleButton; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/components/material-ui/toggleButtonGroup.tsx b/sdnr/wt-odlux/odlux/framework/src/components/material-ui/toggleButtonGroup.tsx
new file mode 100644
index 000000000..bdabe0d56
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/components/material-ui/toggleButtonGroup.tsx
@@ -0,0 +1,40 @@
+/**
+ * ============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 classNames from 'classnames';
+import { Theme } from '@mui/material/styles';
+
+import { WithStyles } from '@mui/styles';
+import withStyles from '@mui/styles/withStyles';
+import createStyles from '@mui/styles/createStyles';
+
+export const styles = (theme: Theme) => createStyles({
+ /* Styles applied to the root element. */
+ root: {
+ transition: theme.transitions.create('background,box-shadow'),
+ background: 'transparent',
+ borderRadius: 2,
+ overflow: 'hidden',
+ },
+ /* Styles applied to the root element if `selected={true}` or `selected="auto" and `value` set. */
+ selected: {
+ background: theme.palette.background.paper,
+ boxShadow: theme.shadows[2],
+ },
+});
+
diff --git a/sdnr/wt-odlux/odlux/framework/src/components/material-ui/treeView.tsx b/sdnr/wt-odlux/odlux/framework/src/components/material-ui/treeView.tsx
new file mode 100644
index 000000000..5c23909c4
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/components/material-ui/treeView.tsx
@@ -0,0 +1,380 @@
+/**
+ * ============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 } from '@mui/material/styles';
+
+import { makeStyles, WithStyles, WithTheme } from '@mui/styles';
+import withStyles from '@mui/styles/withStyles';
+import createStyles from '@mui/styles/createStyles';
+
+import { List, ListItem, TextField, ListItemText, ListItemIcon, Typography } from '@mui/material';
+import { DistributiveOmit } from '@mui/types';
+
+import withTheme from '@mui/styles/withTheme';
+
+import { SvgIconProps } from '@mui/material/SvgIcon';
+import FileIcon from '@mui/icons-material/InsertDriveFile';
+import CloseIcon from '@mui/icons-material/ExpandLess';
+import OpenIcon from '@mui/icons-material/ExpandMore';
+import FolderIcon from '@mui/icons-material/Folder';
+
+declare module '@mui/styles/defaultTheme' {
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface (remove this line if you don't have the rule enabled)
+ interface DefaultTheme extends Theme {}
+}
+
+const styles = (theme: Theme) => createStyles({
+ root: {
+ padding: 0,
+ paddingBottom: 8,
+ paddingTop: 8,
+ },
+ search: {
+ padding: `0px ${theme.spacing(1)}`
+ }
+});
+
+export enum SearchMode {
+ OnKeyDown = 1,
+ OnEnter =2
+}
+
+export type TreeItem<TData = { }> = {
+ disabled?: boolean;
+ icon?: React.ComponentType<SvgIconProps>;
+ iconClass?: string;
+ content: string;
+ contentClass?: string;
+ children?: TreeItem<TData>[];
+ value?: TData;
+}
+
+export type ExternalTreeItem<TData = {}> = TreeItem<TData> & {
+ isMatch?: boolean;
+}
+
+
+type TreeViewComponentState<TData = { }> = {
+ /** All indices of all expanded Items */
+ expandedItems: ExternalTreeItem<TData>[];
+ /** The index of the active iten or undefined if no item is active. */
+ activeItem?: ExternalTreeItem<TData>;
+ /** The search term or undefined if search is currently not active. */
+ searchTerm?: string;
+ searchTermValue?: string;
+}
+
+type TreeViewComponentBaseProps<TData = {}> = WithTheme & WithStyles<typeof styles> & {
+ className?: string;
+ items: TreeItem<TData>[];
+ useFolderIcons?: boolean;
+ enableSearchBar?: boolean;
+ autoExpandFolder?: boolean;
+ style?: React.CSSProperties;
+ itemHeight?: number;
+ depthOffset?: number;
+ searchMode?: SearchMode;
+
+}
+
+type TreeViewComponentWithInternalStateProps<TData = { }> = TreeViewComponentBaseProps<TData> & {
+ initialSearchTerm? : string;
+ onItemClick?: (item: TreeItem<TData>) => void;
+ onFolderClick?: (item: TreeItem<TData>) => void;
+}
+
+type TreeViewComponentWithExternalSearchProps<TData = {}> = TreeViewComponentBaseProps<TData> & {
+ items: ExternalTreeItem<TData>[];
+ initialSearchTerm? : string;
+ searchTerm: string;
+ onSearch: (searchTerm: string) => void;
+ onItemClick?: (item: TreeItem<TData>) => void;
+ onFolderClick?: (item: TreeItem<TData>) => void;
+}
+
+type TreeViewComponentWithExternalStateProps<TData = {}> = TreeViewComponentBaseProps<TData> & TreeViewComponentState<TData> & {
+ items: ExternalTreeItem<TData>[];
+ initialSearchTerm? : string;
+ searchTerm: string;
+ onSearch: (searchTerm: string) => void;
+ onItemClick: (item: TreeItem<TData>) => void;
+ onFolderClick: (item: TreeItem<TData>) => void;
+}
+
+type TreeViewComponentProps<TData = { }> =
+ | TreeViewComponentWithInternalStateProps<TData>
+ | TreeViewComponentWithExternalSearchProps<TData>
+ | TreeViewComponentWithExternalStateProps<TData>;
+
+function isTreeViewComponentWithExternalSearchProps(props: TreeViewComponentProps): props is TreeViewComponentWithExternalSearchProps {
+ const propsWithExternalState = (props as TreeViewComponentWithExternalStateProps)
+ return (
+ propsWithExternalState.onSearch instanceof Function &&
+ propsWithExternalState.onFolderClick === undefined &&
+ propsWithExternalState.expandedItems === undefined &&
+ propsWithExternalState.searchTerm !== undefined
+ );
+}
+
+function isTreeViewComponentWithExternalStateProps(props: TreeViewComponentProps): props is TreeViewComponentWithExternalStateProps {
+ const propsWithExternalState = (props as TreeViewComponentWithExternalStateProps)
+ return (
+ propsWithExternalState.onSearch instanceof Function &&
+ propsWithExternalState.onFolderClick instanceof Function &&
+ propsWithExternalState.expandedItems !== undefined &&
+ propsWithExternalState.searchTerm !== undefined
+ );
+}
+
+class TreeViewComponent<TData = { }> extends React.Component<TreeViewComponentProps<TData>, TreeViewComponentState<TData>> {
+
+ /**
+ * Initializes a new instance.
+ */
+ constructor(props: TreeViewComponentProps<TData>) {
+ super(props);
+
+ this.state = {
+ expandedItems: [],
+ activeItem: undefined,
+ searchTerm: undefined,
+ searchTermValue: props.initialSearchTerm
+ };
+ }
+
+ render(): JSX.Element {
+ this.itemIndex = 0;
+ const { searchTerm , searchTermValue} = this.state;
+ const { children, items, enableSearchBar } = this.props;
+
+ return (
+ <div className={this.props.className ? `${this.props.classes.root} ${this.props.className}` : this.props.classes.root} style={this.props.style}>
+ {children}
+ {enableSearchBar && <TextField variant="standard" label={"Search"} inputProps={{'aria-label': 'treeview-searchfield'}} fullWidth={true} className={this.props.classes.search} value={searchTermValue} onKeyDown={this.onSearchKeyDown} onChange={this.onChangeSearchText} /> || null}
+ {enableSearchBar && (searchTerm === undefined || searchTerm.length===0 )&& <Typography style={{marginTop:'10px'}}>Please search for an inventory identifier or use *.</Typography>}
+ <List>
+ {this.renderItems(items, searchTerm && searchTerm.toLowerCase())}
+ </List>
+ </div>
+ );
+ }
+
+ private itemIndex: number = 0;
+ private renderItems = (items: TreeItem<TData>[], searchTerm: string | undefined, depth: number = 1, forceRender: boolean = true) => {
+
+ return items.reduce((acc, item) => {
+
+ const children = item.children; // this.props.childrenProperty && ((item as any)[this.props.childrenProperty] as TData[]);
+ const childrenJsx = children && this.renderItems(children, searchTerm, depth + 1, this.state.expandedItems.indexOf(item) > -1);
+
+ const expanded = !isTreeViewComponentWithExternalStateProps(this.props) && searchTerm
+ ? childrenJsx && childrenJsx.length > 0
+ : !children
+ ? false
+ : this.state.expandedItems.indexOf(item) > -1;
+ const isFolder = children !== undefined;
+
+ const itemJsx = this.renderItem(item, searchTerm, depth, isFolder, expanded || false, forceRender);
+ itemJsx && acc.push(itemJsx);
+
+ if (isFolder && expanded && childrenJsx) {
+ acc.push(...childrenJsx);
+ }
+ return acc;
+
+ }, [] as JSX.Element[]);
+ }
+ private renderItem = (item: ExternalTreeItem<TData> , searchTerm: string | undefined, depth: number, isFolder: boolean, expanded: boolean, forceRender: boolean): JSX.Element | null => {
+ const styles = {
+ item: {
+ paddingLeft: (((this.props.depthOffset || 0) + depth) * Number(this.props.theme.spacing(3).replace("px", ''))),
+ backgroundColor: this.state.activeItem === item ? this.props.theme.palette.action.selected : undefined,
+ height: this.props.itemHeight || undefined,
+ cursor: item.disabled ? 'not-allowed' : 'pointer',
+ color: item.disabled ? this.props.theme.palette.text.disabled : this.props.theme.palette.text.primary,
+ overflow: 'hidden',
+ transform: 'translateZ(0)',
+ }
+ };
+
+ const text = item.content || ''; // need to keep track of search
+ const search_array = searchTerm?.split("*");
+ const index = search_array?.findIndex(function (_str: String) {
+ return _str.length > 0;
+ }) || 0;
+ const firstSearchSubString = search_array ? search_array[index] : "";
+ const matchIndex = firstSearchSubString ? text.toLowerCase().indexOf(firstSearchSubString) : -1;
+
+ const hasStarInSearch = search_array ? search_array.length > 1 : false;
+ const isSearchStringWithStar = hasStarInSearch && firstSearchSubString?.length > 0 || false;
+
+ const searchTermLength = firstSearchSubString && firstSearchSubString.length || 0;
+
+ const handleClickCreator = (isIcon: boolean) => (event: React.SyntheticEvent) => {
+ if (item.disabled) return;
+ event.preventDefault();
+ event.stopPropagation();
+ if (isFolder && (this.props.autoExpandFolder || isIcon)) {
+ this.props.onFolderClick ? this.props.onFolderClick(item) : this.onFolderClick(item);
+ } else {
+ this.props.onItemClick ? this.props.onItemClick(item) : this.onItemClick(item);
+ }
+ };
+
+ return ((searchTerm && (matchIndex > -1 || expanded || (!isTreeViewComponentWithExternalStateProps(this.props) && item.isMatch || depth === 1)) || !searchTerm || forceRender)
+ ? (
+ <ListItem key={`tree-list-${this.itemIndex++}`} aria-label="tree-view-item" style={styles.item} onClick={handleClickCreator(false)} button >
+
+ { // display the left icon
+ (this.props.useFolderIcons && <ListItemIcon>{isFolder ? <FolderIcon /> : <FileIcon />}</ListItemIcon>) ||
+ (item.icon && (<ListItemIcon className={ item.iconClass }><item.icon /></ListItemIcon>))}
+
+
+ { // highlight search result
+ isSearchStringWithStar && matchIndex > -1
+ ? <ListItemText className={item.contentClass} primary={(
+ <span
+ style={{
+ display: 'inline-block',
+ backgroundColor: 'rgba(255,235,59,0.5)',
+ padding: '3px',
+ }}
+ >
+ {text}
+ </span>)} />
+ : matchIndex > -1
+ ? <ListItemText className={item.contentClass} primary={(<span>
+ {text.substring(0, matchIndex)}
+ <span
+ style={{
+ display: 'inline-block',
+ backgroundColor: 'rgba(255,235,59,0.5)',
+ padding: '3px',
+ }}
+ >
+ {text.substring(matchIndex, matchIndex + searchTermLength)}
+ </span>
+ {text.substring(matchIndex + searchTermLength)}
+ </span>)} />
+ : <ListItemText className={item.contentClass} primary={(
+ <span style={item.isMatch ? {
+ display: 'inline-block',
+ padding: '3px',
+ } : undefined}>
+ {text} </span>
+ )} />
+ }
+
+ { // display the right icon, depending on the state
+ !isFolder ? null : expanded ? (<OpenIcon onClick={handleClickCreator(true)} />) : (<CloseIcon onClick={handleClickCreator(true)} />)}
+ </ListItem>
+ )
+ : null
+ );
+ }
+
+ private onFolderClick = (item: TreeItem<TData>) => {
+ // toggle items with children
+ if (this.state.searchTerm) return;
+ const indexOfItemToToggle = this.state.expandedItems.indexOf(item);
+ if (indexOfItemToToggle === -1) {
+ this.setState({
+ expandedItems: [...this.state.expandedItems, item],
+ });
+ } else {
+ this.setState({
+ expandedItems: [
+ ...this.state.expandedItems.slice(0, indexOfItemToToggle),
+ ...this.state.expandedItems.slice(indexOfItemToToggle + 1),
+ ]
+ });
+ }
+ };
+
+ private onItemClick = (item: TreeItem<TData>) => {
+ // activate items without children
+ this.setState({
+ activeItem: item,
+ });
+ };
+
+ private onSearchKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
+ const enterMode = this.props.searchMode === SearchMode.OnEnter;
+
+ if (enterMode && event.keyCode === 13) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ enterMode && this.setState({
+ searchTerm: this.state.searchTermValue
+ });
+
+ if (isTreeViewComponentWithExternalSearchProps(this.props) || isTreeViewComponentWithExternalStateProps(this.props)) {
+ this.props.onSearch(this.state.searchTermValue || "");
+ }
+ }
+ }
+
+ private onChangeSearchText = (event: React.ChangeEvent<HTMLInputElement>) => {
+ event.preventDefault();
+ event.stopPropagation();
+
+ const keyDownMode = (!this.props.searchMode || this.props.searchMode === SearchMode.OnKeyDown);
+
+ this.setState(keyDownMode
+ ? {
+ searchTerm: event.target.value,
+ searchTermValue: event.target.value,
+ } as any : {
+ searchTermValue: event.target.value,
+ }) as any;
+
+ if ((isTreeViewComponentWithExternalSearchProps(this.props) || isTreeViewComponentWithExternalStateProps(this.props)) && keyDownMode) {
+ this.props.onSearch(event.target.value);
+ }
+ };
+
+ static getDerivedStateFromProps(props: TreeViewComponentProps, state: TreeViewComponentState): TreeViewComponentState {
+ if (isTreeViewComponentWithExternalStateProps(props)) {
+ return {
+ ...state,
+ expandedItems: props.expandedItems || [],
+ activeItem: props.activeItem,
+ searchTerm: props.searchTerm
+ };
+ } else if (isTreeViewComponentWithExternalSearchProps(props)) {
+ return {
+ ...state,
+ searchTerm: props.searchTerm,
+ };
+ }
+ return state;
+ }
+
+ public static defaultProps = {
+ useFolderIcons: false,
+ enableSearchBar: false,
+ autoExpandFolder: false,
+ depthOffset: 0
+ }
+}
+
+export type TreeViewCtorType<TData = { }> = new () => React.Component<DistributiveOmit<TreeViewComponentProps<TData>, 'theme'|'classes'>>;
+
+export const TreeView = withTheme(withStyles(styles)(TreeViewComponent));
+export default TreeView; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/components/navigationMenu.tsx b/sdnr/wt-odlux/odlux/framework/src/components/navigationMenu.tsx
new file mode 100644
index 000000000..bcc191a89
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/components/navigationMenu.tsx
@@ -0,0 +1,218 @@
+/**
+ * ============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 { Theme } from '@mui/material/styles';
+import classNames from 'classnames';
+
+import { WithStyles } from '@mui/styles';
+import withStyles from '@mui/styles/withStyles';
+import createStyles from '@mui/styles/createStyles';
+
+import Drawer from '@mui/material/Drawer';
+import List from '@mui/material/List';
+
+import Divider from '@mui/material/Divider';
+
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faProjectDiagram } from '@fortawesome/free-solid-svg-icons';
+
+import ListItemLink from '../components/material-ui/listItemLink';
+
+import { connect, Connect } from '../flux/connect';
+import { MenuAction } from '../actions/menuAction';
+import { transportPCEUrl } from '../app';
+
+const aboutIcon = require('../assets/icons/About.svg');
+const homeIcon = require('../assets/icons/Home.svg');
+const loginIcon = require('../assets/icons/User.svg');
+const settingsIcon = require('../assets/icons/Tools.svg');
+
+const drawerWidth = 240;
+
+const extraLinks = (window as any)._odluxExtraLinks as [string, string][];
+
+const styles = (theme: Theme) => createStyles({
+ drawerPaper: {
+ position: 'relative',
+ width: drawerWidth,
+ },
+ toolbar: theme.mixins.toolbar as any,
+
+ drawerOpen: {
+ width: drawerWidth,
+ transition: theme.transitions.create('width', {
+ easing: theme.transitions.easing.sharp,
+ duration: theme.transitions.duration.enteringScreen,
+ }),
+ },
+ drawerClose: {
+ transition: theme.transitions.create('width', {
+ easing: theme.transitions.easing.sharp,
+ duration: theme.transitions.duration.leavingScreen,
+ }),
+ overflowX: 'hidden',
+ width: theme.spacing(7),
+ [theme.breakpoints.up('sm')]: {
+ width: theme.spacing(9),
+ },
+ },
+ drawer: {
+
+ },
+ menu: {
+ flex: "1 0 0%",
+ },
+ optLinks: {
+ borderTop: "2px solid #cfcfcf",
+ display: "flex",
+ flexDirection: "row",
+ flexWrap: "wrap",
+ justifyContent: "space-around"
+ },
+ link: {
+ margin: theme.spacing(1)+1,
+ fontSize: theme.typography.fontSize-2,
+ },
+});
+
+const tabletWidthBreakpoint = 768;
+
+export const NavigationMenu = withStyles(styles)(connect()(({ classes, state, dispatch }: WithStyles<typeof styles> & Connect & Connect) => {
+ const { user } = state.framework.authenticationState;
+ const isOpen = state.framework.applicationState.isMenuOpen;
+ const closedByUser = state.framework.applicationState.isMenuClosedByUser;
+ const transportUrl = state.framework.applicationState.transportpceUrl;
+
+ const [responsive, setResponsive] = React.useState(false);
+
+ //collapse menu on mount if necessary
+ React.useEffect(()=>{
+
+ if(isOpen && window.innerWidth <= tabletWidthBreakpoint){
+
+ setResponsive(true);
+ dispatch(new MenuAction(false));
+ }
+
+ },[]);
+
+ React.useEffect(() => {
+
+ function handleResize() {
+ if (user && user.isValid) {
+ if (window.innerWidth < tabletWidthBreakpoint && !responsive) {
+ setResponsive(true);
+ if (!closedByUser) {
+ dispatch(new MenuAction(false));
+ }
+
+ } else if (window.innerWidth > tabletWidthBreakpoint && responsive) {
+ setResponsive(false);
+ if (!closedByUser) {
+ dispatch(new MenuAction(true));
+ }
+
+ }
+ }
+ }
+ window.addEventListener("resize", handleResize);
+
+
+ return () => {
+ window.removeEventListener("resize", handleResize);
+ }
+ })
+
+ React.useEffect(()=>{
+ // trigger a resize if menu changed in case elements have to re-arrange
+ window.dispatchEvent(new Event('menu-resized'));
+ }, [isOpen])
+
+ let menuItems = state.framework.applicationRegistration && Object.keys(state.framework.applicationRegistration).map(key => {
+ const reg = state.framework.applicationRegistration[key];
+ return reg && (
+ <ListItemLink
+ key={reg.name}
+ to={reg.path || `/${reg.name}`}
+ primary={reg.menuEntry || reg.name}
+ secondary={reg.subMenuEntry}
+ icon={reg.icon || null} />
+ ) || null;
+ }) || null;
+
+ if(transportUrl.length>0){
+
+ const transportPCELink = <ListItemLink
+ key={"transportPCE"}
+ to={transportUrl}
+ primary={"TransportPCE"}
+ icon={faProjectDiagram}
+ external />;
+
+ const linkFound = menuItems.find(obj => obj.key === "microwave");
+
+ if (linkFound) {
+ const index = menuItems.indexOf(linkFound);
+ menuItems.splice(index + 1, 0, transportPCELink);
+ } else {
+ menuItems.push(transportPCELink);
+ }
+ }
+
+
+ return (
+ <Drawer
+ variant="permanent"
+ className={
+ classNames(classes.drawer, {
+ [classes.drawerOpen]: isOpen,
+ [classes.drawerClose]: !isOpen
+ })
+ }
+ classes={{
+ paper: classes.drawerPaper,
+ }}
+ >
+ {user && user.isValid && <>
+ <div className={classes.toolbar} />
+ { /* https://fiffty.github.io/react-treeview-mui/ */}
+ <List className={classes.menu} component="nav">
+ <ListItemLink exact to="/" primary="Home" icon={homeIcon} />
+ <Divider />
+ {
+ menuItems
+ }
+ <Divider />
+ <ListItemLink to="/about" primary="About" icon={aboutIcon} />
+ {(false && process.env.NODE_ENV === "development")
+ ? <>
+ <Divider />
+ <ListItemLink to="/test" primary="Test" icon={settingsIcon} />
+ </>
+ : null
+ }
+ </List>
+ {isOpen && extraLinks && <div className={classes.optLinks}>
+ {extraLinks.map(linkInfo => (<a className={classes.link} href={linkInfo[1]}>{linkInfo[0]}</a>))}
+ </div> || null}
+ </> || null
+ }
+ </Drawer>)
+}));
+
+export default NavigationMenu; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/components/objectDump/index.tsx b/sdnr/wt-odlux/odlux/framework/src/components/objectDump/index.tsx
new file mode 100644
index 000000000..10a0547be
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/components/objectDump/index.tsx
@@ -0,0 +1,205 @@
+/**
+ * ============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 makeStyles from '@mui/styles/makeStyles';
+
+export const getTypeName = (obj: any): string => {
+ if (obj == null) {
+ return obj === undefined ? "Undefined" : "Null";
+ }
+ return Object.prototype.toString.call(obj).slice(8, -1);
+};
+
+const isObjectLike = (obj: any) => {
+ return typeof obj === "object" && obj !== null;
+};
+
+const isBoolean = (obj: any) => {
+ return obj === true || obj === false ||
+ (isObjectLike(obj) && getTypeName(obj) === "Boolean");
+};
+
+const isNumber = (obj: any) => {
+ return typeof obj === "number" ||
+ (isObjectLike(obj) && getTypeName(obj) === "Number");
+};
+
+const isString = (obj: any) => {
+ return typeof obj === "string" ||
+ (isObjectLike(obj) && getTypeName(obj) === "String");
+};
+
+const isNull = (obj: any) => {
+ return obj === null;
+};
+
+const isDate = (obj: any): boolean => {
+ return isObjectLike(obj) && (obj instanceof Date);
+};
+
+const useSimpleTableStyles = makeStyles({
+ root: {
+ },
+ table: {
+ fontFamily: "verdana, arial, helvetica, sans-serif",
+ borderSpacing: "3px",
+ borderCollapse: "separate",
+ marginLeft: "30px"
+ },
+ label: {
+ cursor: "pointer",
+ },
+ th: {
+ textAlign: "left",
+ color: "white",
+ padding: "5px",
+ backgroundColor: "#cccccc",
+
+ },
+ td: {
+ verticalAlign: "top",
+ padding: "0.5rem 1rem",
+ border: "2px solid #DDD"
+ },
+ object: {
+ },
+ objectTh: {
+ backgroundColor: "#cccccc",
+ },
+ objectTd: {
+ padding: "0.5rem 1rem",
+ border: "2px solid #DDD"
+ },
+ chevron: {
+ '&:before': {
+ borderStyle: 'solid',
+ borderWidth: '0.25em 0.25em 0 0',
+ content: '\'\'',
+ display: 'inline-block',
+ height: '0.45em',
+ left: '0.15em',
+ position: 'relative',
+ top: '0.15em',
+ transform: 'rotate(-45deg)',
+ transition: 'all 0.3s',
+ verticalAlign: 'top',
+ width: '0.45em',
+ }
+
+ },
+ right: {
+ '&:before': {
+ left: '0',
+ transform: 'rotate(45deg)',
+ }
+ },
+ bottom: {
+ '&:before': {
+ left: '0',
+ transform: 'rotate(135deg)',
+ }
+ },
+});
+
+
+type SimpleTableProps = {
+ classNameTh?: string;
+ label?: JSX.Element | string | null;
+ cols?: number;
+ expand?: boolean;
+ ariaLabel?: string;
+}
+
+const SimpleTable: React.FC<SimpleTableProps> = (props) => {
+ const { label = '', cols = 2, expand = true, classNameTh, children } = props;
+ const [isExpanded, setIsExpanded] = React.useState(expand);
+
+ const classes = useSimpleTableStyles();
+
+ React.useEffect(() => {
+ setIsExpanded(expand);
+ }, [expand]);
+
+ const handleClick = () => {
+ setIsExpanded(!isExpanded);
+ };
+
+ return (
+ <table className={`${classes.root} ${classes.table}`} aria-label={props.ariaLabel? props.ariaLabel+'-table' : 'table'}>
+ {label && (<thead>
+ <tr aria-label={props.ariaLabel? props.ariaLabel+'-title-row' : 'title row'}>
+ <th className={`${classes.th} ${classes.label} ${classNameTh ? classNameTh : ''}`} colSpan={cols} onClick={handleClick}>
+ <span className={`${classes.chevron} ${isExpanded ? classes.bottom : classes.right }`}></span> { label }
+ </th>
+ </tr>
+ </thead>) || null
+ }
+ {isExpanded && <tbody >{children}</tbody> || null}
+ </table>
+ );
+};
+
+
+type ObjectRendererProps = {
+ className?: string;
+ label?: JSX.Element | string | null;
+ expand?: boolean;
+ object: { [key: string]: any };
+ ariaLabel?: string;
+};
+
+const ObjectRenderer: React.FC<ObjectRendererProps> = (props) => {
+ const { object, className, label = 'Object', expand = true } = props;
+ const classes = useSimpleTableStyles();
+
+ return (
+ <SimpleTable ariaLabel={props.ariaLabel} classNameTh={classes.objectTh} label={getTypeName(object) || label} expand={expand}>
+ {
+ Object.keys(object).map(key => {
+ return (
+ <tr key={String(key)} aria-label={props.ariaLabel? props.ariaLabel+'-row': 'row'}>
+ <td className={`${classes.td} ${classes.objectTd}`} aria-label="object-title">{String(key)} </td>
+ <td className={`${classes.td}`} aria-label="object-details">{renderObject(object[key], "sub-element")}</td>
+ </tr>
+ );
+ })
+ }
+ </SimpleTable>
+ );
+};
+
+
+type ArrayRendererProps = {
+ label?: JSX.Element | string | null;
+ extraRenderer?: { [label: string]: React.ComponentType<{ label?: JSX.Element | string | null; object: any; }> };
+ description?: string;
+ object: any;
+};
+
+const ArrayRenderer: React.FC<ArrayRendererProps> = (props) => {
+
+ return null;
+};
+
+export const renderObject = (object: any, ariaLabel?: string): JSX.Element | string => {
+ if (isString(object) || isNumber(object) || isBoolean(object)) {
+ return String(object);
+ }
+ return <ObjectRenderer object={object} ariaLabel={ariaLabel} />;
+};
diff --git a/sdnr/wt-odlux/odlux/framework/src/components/routing/appFrame.tsx b/sdnr/wt-odlux/odlux/framework/src/components/routing/appFrame.tsx
new file mode 100644
index 000000000..aa22f17f4
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/components/routing/appFrame.tsx
@@ -0,0 +1,55 @@
+/**
+ * ============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 { connect, Connect } from '../../flux/connect';
+
+import { SetTitleAction } from '../../actions/titleActions';
+import { AddErrorInfoAction } from '../../actions/errorActions';
+
+import { IconType } from '../../models/iconDefinition';
+
+export interface IAppFrameProps {
+ title: string;
+ icon?: IconType;
+ appId?: string
+}
+
+/**
+ * Represents a component to wich will embed each single app providing the
+ * functionality to update the title and implement an exeprion border.
+ */
+export class AppFrame extends React.Component<IAppFrameProps & Connect> {
+
+ public render(): JSX.Element {
+ return (
+ <div style={{ flex: "1", overflow: "hidden", display: "flex", flexDirection: "column" }}>
+ { this.props.children }
+ </div>
+ )
+ }
+
+ public componentDidMount() {
+ this.props.dispatch(new SetTitleAction(this.props.title, this.props.icon, this.props.appId));
+ }
+ public componentDidCatch(error: Error | null, info: object) {
+ this.props.dispatch(new AddErrorInfoAction({ error, info }));
+ }
+}
+
+export default connect()(AppFrame); \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/components/settings/general.tsx b/sdnr/wt-odlux/odlux/framework/src/components/settings/general.tsx
new file mode 100644
index 000000000..ffd516ba1
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/components/settings/general.tsx
@@ -0,0 +1,110 @@
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ */
+
+import React from 'react';
+import { Button, FormControlLabel, Switch, Typography } from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import { SettingsComponentProps } from '../../models/settings';
+import { connect, Connect, IDispatcher } from '../../flux/connect';
+import { IApplicationStoreState } from '../../store/applicationStore';
+import { getGeneralSettingsAction, SetGeneralSettingsAction, updateGeneralSettingsAction } from '../../actions/settingsAction';
+import { sendMessage, SettingsMessage } from '../../services/broadcastService';
+
+
+type props = Connect<typeof mapProps, typeof mapDispatch> & SettingsComponentProps;
+
+const mapProps = (state: IApplicationStoreState) => ({
+ settings: state.framework.applicationState.settings,
+ user: state.framework.authenticationState.user?.user
+
+});
+
+const mapDispatch = (dispatcher: IDispatcher) => ({
+
+ updateSettings :(activateNotifications: boolean) => dispatcher.dispatch(updateGeneralSettingsAction(activateNotifications)),
+ getSettings: () =>dispatcher.dispatch(getGeneralSettingsAction()),
+ });
+
+const styles = makeStyles({
+ sectionMargin: {
+ marginTop: "30px",
+ marginBottom: "15px"
+ },
+ elementMargin: {
+ marginLeft: "10px"
+ },
+ buttonPosition:{
+ position: "absolute",
+ right: "32%"
+ }
+ });
+
+const General : React.FunctionComponent<props> = (props) =>{
+
+const classes = styles();
+
+const [areWebsocketsEnabled, setWebsocketsEnabled] = React.useState(props.settings.general.areNotificationsEnabled || false);
+
+React.useEffect(()=>{
+ props.getSettings();
+},[]);
+
+React.useEffect(()=>{
+ if(props.settings.general.areNotificationsEnabled!==null)
+ setWebsocketsEnabled(props.settings.general.areNotificationsEnabled)
+},[props.settings]);
+
+const onWebsocketsChange = (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>, newValue: boolean) =>{
+ setWebsocketsEnabled(newValue);
+ }
+
+const onSave = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) =>{
+
+ e.preventDefault();
+ const message: SettingsMessage = {key: 'general', enableNotifications: areWebsocketsEnabled, user: props.user!};
+ sendMessage(message, "odlux_settings");
+ props.updateSettings(areWebsocketsEnabled);
+ props.onClose();
+}
+
+const onCancel = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) =>{
+ e.preventDefault();
+ props.onClose();
+
+}
+
+
+ return <div>
+ <Typography className={classes.sectionMargin} variant="body1" style={{ fontWeight: "bold" }} gutterBottom>
+ Enable Notifications
+ </Typography>
+ <FormControlLabel style={{ padding:5}}
+ value="end"
+ control={<Switch color="secondary" aria-label="enable-notifications-button" aria-checked={areWebsocketsEnabled} checked={areWebsocketsEnabled} onChange={onWebsocketsChange} />}
+ label="Enable Notifications"
+ labelPlacement="end"
+ />
+ <div className={classes.buttonPosition}>
+ <Button aria-label="cancel-button" className={classes.elementMargin} variant="contained" color="primary" onClick={onCancel}>Cancel</Button>
+ <Button aria-label="save-button" className={classes.elementMargin} variant="contained" color="secondary" onClick={onSave}>Save</Button>
+ </div>
+ </div>
+}
+
+export const GeneralUserSettings = connect(mapProps, mapDispatch)(General);
+export default GeneralUserSettings; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/components/titleBar.tsx b/sdnr/wt-odlux/odlux/framework/src/components/titleBar.tsx
new file mode 100644
index 000000000..40c0fc736
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/components/titleBar.tsx
@@ -0,0 +1,233 @@
+/**
+ * ============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 { withRouter, RouteComponentProps } from 'react-router-dom';
+
+import { Theme } from '@mui/material/styles';
+import { WithStyles } from '@mui/styles';
+import withStyles from '@mui/styles/withStyles';
+import createStyles from '@mui/styles/createStyles';
+import AppBar from '@mui/material/AppBar';
+import Toolbar from '@mui/material/Toolbar';
+import Typography from '@mui/material/Typography';
+import Button from '@mui/material/Button';
+import IconButton from '@mui/material/IconButton';
+import AccountCircle from '@mui/icons-material/AccountCircle';
+import MenuItem from '@mui/material/MenuItem';
+import Menu from '@mui/material/Menu';
+
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faBan } from '@fortawesome/free-solid-svg-icons';
+import { faDotCircle } from '@fortawesome/free-solid-svg-icons';
+
+import { logoutUser } from '../actions/authentication';
+import { PushAction, ReplaceAction } from '../actions/navigationActions';
+
+import { connect, Connect, IDispatcher } from '../flux/connect';
+import { MenuAction, MenuClosedByUser } from '../actions/menuAction';
+
+import MenuIcon from './icons/menuIcon';
+import Logo from './logo';
+
+const styles = (theme: Theme) => createStyles({
+ appBar: {
+ zIndex: theme.zIndex.drawer + 1,
+ },
+ grow: {
+ flexGrow: 1,
+ },
+ menuButton: {
+ marginLeft: -12,
+ marginRight: 20,
+ },
+ icon: {
+ marginLeft: 16,
+ marginRight: 8,
+ marginBottom: -2,
+ },
+ connected: {
+ color: "green"
+ },
+ notConnected: {
+ color: "red"
+ },
+ notificationInfo: {
+ marginLeft: 5
+ }
+});
+
+const mapDispatch = (dispatcher: IDispatcher) => {
+ return {
+ logout: () => {
+ dispatcher.dispatch(logoutUser());
+ dispatcher.dispatch(new ReplaceAction("/login"));
+ },
+ openSettings : () =>{
+ dispatcher.dispatch(new PushAction("/settings"));
+ },
+ toggleMainMenu: (value: boolean, value2: boolean) => {
+ dispatcher.dispatch(new MenuAction(value));
+ dispatcher.dispatch(new MenuClosedByUser(value2))
+ }
+ }
+};
+
+type TitleBarProps = RouteComponentProps<{}> & WithStyles<typeof styles> & Connect<undefined, typeof mapDispatch>
+
+class TitleBarComponent extends React.Component<TitleBarProps, { anchorEl: HTMLElement | null }> {
+
+ constructor(props: TitleBarProps) {
+ super(props);
+ this.state = {
+ anchorEl: null
+ }
+
+ }
+ render(): JSX.Element {
+ const { classes, state, history, location } = this.props;
+ const open = !!this.state.anchorEl;
+ let toolbarElements: Array<JSX.Element>;
+ toolbarElements = [];
+
+ // create notificationInfo element
+ const notificationInfo = state.framework.applicationState.isWebsocketAvailable != undefined ?
+ (state.framework.applicationState.isWebsocketAvailable ?
+ <Typography aria-label="notifications-are-active" variant="body1" className={classes.notificationInfo}>Notifications <FontAwesomeIcon className={classes.connected} icon={faDotCircle} /> |</Typography> : <Typography aria-label="notifications-are-inactive" variant="body1" className={classes.notificationInfo}>Notifications <FontAwesomeIcon className={classes.notConnected} icon={faBan} /> |</Typography>)
+ : <Typography variant="body1" aria-label="notifications-are-not-available" className={classes.notificationInfo}>Notifications N/A |</Typography>;
+
+
+ // add notificationInfo element before help
+ if (state.framework.applicationRegistration) {
+ let isNotificationInfoAdded = false;
+ Object.keys(state.framework.applicationRegistration).map(key => {
+ const reg = state.framework.applicationRegistration[key];
+ if (reg && reg.statusBarElement) {
+ if (key === "help") {
+ isNotificationInfoAdded = true;
+ toolbarElements.push(notificationInfo);
+ }
+ toolbarElements.push(<reg.statusBarElement key={key} />);
+ }
+ });
+
+ // add notificationInfo in case help wasn't found
+ if (!isNotificationInfoAdded) {
+ toolbarElements.push(notificationInfo);
+ }
+ }
+
+ const stateIcon = state.framework.applicationState.icon;
+ const customIconHeight = 22;
+ const icon = !stateIcon
+ ? null
+ : (typeof stateIcon === 'string'
+ ? <img className={classes.icon} height={customIconHeight} src={stateIcon} />
+ : <FontAwesomeIcon className={classes.icon} icon={stateIcon} />)
+
+
+ return (
+ <AppBar enableColorOnDark position="absolute" className={classes.appBar}>
+ <Toolbar>
+ <IconButton
+ className={classes.menuButton}
+ color="inherit"
+ aria-label="Menu"
+ onClick={this.toggleMainMenu}
+ size="large">
+ <MenuIcon />
+ </IconButton>
+ <Logo />
+ <Typography variant="h6" color="inherit" >
+ {icon}
+ {state.framework.applicationState.title}
+ </Typography>
+ <div className={classes.grow}></div>
+ {
+ // render toolbar
+ toolbarElements.map((item) => {
+ return item
+ })
+ }
+
+ {state.framework.authenticationState.user
+ ? (<div>
+ <Button aria-label="current user menu button"
+ aria-owns={open ? 'menu-appbar' : undefined}
+ aria-haspopup="true"
+ onClick={this.openMenu}
+ color="inherit"
+ >
+ <AccountCircle />
+ {state.framework.authenticationState.user.user}
+ </Button>
+ <Menu
+ id="menu-appbar"
+ anchorEl={this.state.anchorEl}
+ anchorOrigin={{
+ vertical: 'top',
+ horizontal: 'right',
+ }}
+ transformOrigin={{
+ vertical: 'top',
+ horizontal: 'right',
+ }}
+ open={open}
+ onClose={this.closeMenu}
+ >
+ {/* <MenuItem onClick={ this.closeMenu }>Profile</MenuItem> */}
+ <MenuItem
+ aria-label="settings-button"
+ onClick={ () =>{
+ this.props.openSettings();
+ this.closeMenu(); }}>Settings</MenuItem>
+ <MenuItem
+ aria-label="logout-button"
+ onClick={() => {
+ this.props.logout();
+ this.closeMenu();
+ }}>Logout</MenuItem>
+ </Menu>
+ </div>)
+ : (<Button onClick={() => { history.push('/login') }} color="inherit" disabled={location.pathname == "/login"}>Login</Button>)}
+ </Toolbar>
+ </AppBar>
+ );
+ };
+
+ private toggleMainMenu = (event: React.MouseEvent<HTMLElement>) => {
+ console.log(this.props);
+ if (this.props.state.framework.authenticationState.user && this.props.state.framework.authenticationState.user.isValid) {
+ const isMainMenuOpen = this.props.state.framework.applicationState.isMenuOpen
+ const isClosedByUser = this.props.state.framework.applicationState.isMenuClosedByUser
+ this.props.toggleMainMenu(!isMainMenuOpen, !isClosedByUser);
+ }
+ }
+
+ private openMenu = (event: React.MouseEvent<HTMLElement>) => {
+ this.setState({ anchorEl: event.currentTarget });
+ };
+
+ private closeMenu = () => {
+ this.setState({ anchorEl: null });
+ };
+}
+
+//todo: ggf. https://github.com/acdlite/recompose verwenden zur Vereinfachung
+
+export const TitleBar = withStyles(styles)(withRouter(connect(undefined, mapDispatch)(TitleBarComponent)));
+export default TitleBar; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/design/default.ts b/sdnr/wt-odlux/odlux/framework/src/design/default.ts
new file mode 100644
index 000000000..c4a8118c2
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/design/default.ts
@@ -0,0 +1,81 @@
+/**
+ * ============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==========================================================================
+ */
+/******************************************************************************
+ * Copyright 2018 highstreet technologies GmbH
+ *
+ * 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.
+ *****************************************************************************/
+
+import { createTheme, adaptV4Theme } from '@mui/material/styles';
+import onapLogo from '../assets/images/onapLogo.gif'
+
+const theme = createTheme(adaptV4Theme({
+ design: {
+ id: "onap",
+ name: "Open Networking Automation Plattform (ONAP)",
+ url: onapLogo,
+ height: 49,
+ width: 229,
+ logoHeight: 32,
+ },
+ palette: {
+ primary: {
+ light: "#eeeeee",
+ main: "#ffffff",
+ dark: "#e0e0e0",
+ contrastText: "#07819B"
+ },
+ secondary: {
+ light: "rgba(7, 129, 155, 94)",
+ main: "rgba(7, 129, 155, 201)",
+ dark: "#07819B",
+ contrastText: "#ffffff"
+ },
+ },
+ overrides: { //temp fix for labels turning white after material new version (palette primary color)
+ MuiFormLabel: {
+ root: {
+ "&.Mui-focused": {
+ color: "rgba(143,143,143,1)"
+ }
+ },
+
+ focused: {}
+ },
+ MuiInput: {
+ underline: {
+
+ "&:after": {
+ borderBottom: "2px solid #444444"
+ }
+ }
+ }
+ },
+}));
+
+export default theme; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/favicon.ico b/sdnr/wt-odlux/odlux/framework/src/favicon.ico
new file mode 100644
index 000000000..a8a5d31ca
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/favicon.ico
Binary files differ
diff --git a/sdnr/wt-odlux/odlux/framework/src/flux/action.ts b/sdnr/wt-odlux/odlux/framework/src/flux/action.ts
new file mode 100644
index 000000000..76890257e
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/flux/action.ts
@@ -0,0 +1,26 @@
+/**
+ * ============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 an action in the odlux flux architecture.
+ */
+export abstract class Action { }
+
+export interface IActionHandler<TState, TAction extends Action = Action> {
+ (state: TState | undefined, action: TAction): TState;
+} \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/flux/connect.tsx b/sdnr/wt-odlux/odlux/framework/src/flux/connect.tsx
new file mode 100644
index 000000000..09d30dae7
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/flux/connect.tsx
@@ -0,0 +1,213 @@
+/**
+ * ============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, { FC, useContext, createContext, useState, useEffect, useRef } from 'react';
+
+import { Dispatch } from './store';
+
+import { ApplicationStore, IApplicationStoreState } from '../store/applicationStore';
+
+const LogLevel = +(localStorage.getItem('log.odlux.framework.flux.connect') || 0);
+
+interface IApplicationStoreContext {
+ applicationStore: ApplicationStore;
+}
+
+export interface IDispatcher {
+ dispatch: Dispatch;
+}
+
+interface IApplicationStoreProps {
+ state: IApplicationStoreState;
+}
+
+interface IDispatchProps {
+ dispatch: Dispatch;
+}
+
+type ComponentDecoratorInfer<TMergedProps> = {
+ <TProps>(wrappedComponent: React.ComponentType<TProps & TMergedProps>): React.ComponentClass<Omit<TProps & TMergedProps, keyof TMergedProps>>;
+};
+
+const ApplicationStoreContext = createContext<IApplicationStoreContext | undefined>(undefined);
+
+export type Connect<TMapProps extends ((...args: any) => any) | undefined = undefined, TMapDispatch extends ((...args: any) => any) | undefined = undefined> =
+ (TMapProps extends ((...args: any) => any) ? ReturnType<TMapProps> : IApplicationStoreProps) &
+ (TMapDispatch extends ((...args: any) => any) ? ReturnType<TMapDispatch> : IDispatchProps);
+
+export function connect(): ComponentDecoratorInfer<IApplicationStoreProps & IDispatchProps>;
+
+export function connect<TStateProps>(
+ mapStateToProps: (state: IApplicationStoreState) => TStateProps
+): ComponentDecoratorInfer<TStateProps & IDispatchProps>;
+
+export function connect<TStateProps, TDispatchProps>(
+ mapStateToProps: (state: IApplicationStoreState) => TStateProps,
+ mapDispatchToProps: (dispatcher: IDispatcher) => TDispatchProps
+): ComponentDecoratorInfer<TStateProps & TDispatchProps>;
+
+
+export function connect<TDispatchProps>(
+ mapStateToProps: undefined,
+ mapDispatchToProps: (dispatcher: IDispatcher) => TDispatchProps
+): ComponentDecoratorInfer<IApplicationStoreProps & TDispatchProps>;
+
+
+export function connect<TProps, TStateProps, TDispatchProps>(
+ mapStateToProps?: ((state: IApplicationStoreState) => TStateProps),
+ mapDispatchToProps?: ((dispatcher: IDispatcher) => TDispatchProps)
+):
+ ((WrappedComponent: React.ComponentType<TProps & (IApplicationStoreProps | TStateProps) & IDispatchProps>) => React.ComponentType<TProps>) {
+
+ const injectApplicationStore = (WrappedComponent: React.ComponentType<TProps & (IApplicationStoreProps | TStateProps) & IDispatchProps>): React.ComponentType<TProps> => {
+
+ class StoreAdapter extends React.Component<TProps, {}> {
+
+ render(): JSX.Element {
+
+ if (isWrappedComponentIsVersion1(WrappedComponent)) {
+ const element = React.createElement(WrappedComponent, { ...(this.props as any), state: this.store.state, dispatch: this.store.dispatch.bind(this.store) });
+ return element;
+ } else if (mapStateToProps && isWrappedComponentIsVersion2(WrappedComponent)) {
+ const element = React.createElement(WrappedComponent, { ...(this.props as any), ...(mapStateToProps(this.store.state) as any), dispatch: this.store.dispatch.bind(this.store) });
+ return element;
+ } else if (mapStateToProps && mapDispatchToProps && isWrappedComponentIsVersion3(WrappedComponent)) {
+ const element = React.createElement(WrappedComponent, { ...(this.props as any), ...(mapStateToProps(this.store.state) as any), ...(mapDispatchToProps({ dispatch: this.store.dispatch.bind(this.store) }) as any) });
+ return element;
+ } else if (!mapStateToProps && mapDispatchToProps && isWrappedComponentIsVersion4(WrappedComponent)) {
+ const element = React.createElement(WrappedComponent, { ...(this.props as any), state: this.store.state, ...(mapDispatchToProps({ dispatch: this.store.dispatch.bind(this.store) }) as any) });
+ return element;
+ }
+ throw new Error("Invalid arguments in connect.");
+ }
+
+ componentDidMount(): void {
+ this.store && this.store.changed.addHandler(this.handleStoreChanged);
+ }
+
+ componentWillUnmount(): void {
+ this.store && this.store.changed.removeHandler(this.handleStoreChanged);
+ }
+
+ private get store(): ApplicationStore {
+ return this.context.applicationStore;
+ }
+
+ private handleStoreChanged = () => {
+ this.forceUpdate();
+ }
+ }
+ StoreAdapter.contextType = ApplicationStoreContext;
+ return StoreAdapter;
+ }
+
+
+ return injectApplicationStore;
+
+ /* inline methods */
+
+ function isWrappedComponentIsVersion1(wrappedComponent: any): wrappedComponent is React.ComponentType<TProps & IApplicationStoreProps & IDispatchProps> {
+ return !mapStateToProps && !mapDispatchToProps;
+ }
+
+ function isWrappedComponentIsVersion2(wrappedComponent: any): wrappedComponent is React.ComponentType<TProps & TStateProps & IDispatchProps> {
+ return !!mapStateToProps && !mapDispatchToProps;
+ }
+
+ function isWrappedComponentIsVersion3(wrappedComponent: any): wrappedComponent is React.ComponentType<TProps & TStateProps & TDispatchProps> {
+ return !!mapStateToProps && !!mapDispatchToProps;
+ }
+
+ function isWrappedComponentIsVersion4(wrappedComponent: any): wrappedComponent is React.ComponentType<TProps & TStateProps & TDispatchProps> {
+ return !mapStateToProps && !!mapDispatchToProps;
+ }
+}
+
+type ApplicationStoreProviderProps = {
+ applicationStore: ApplicationStore;
+}
+
+export const ApplicationStoreProvider: FC<ApplicationStoreProviderProps> = (props) => {
+ const { applicationStore, children } = props;
+
+ return (
+ <ApplicationStoreContext.Provider value={{ applicationStore }}>
+ {children}
+ </ApplicationStoreContext.Provider>
+ );
+};
+
+export const useApplicationStore = (): ApplicationStore => {
+ const context = useContext(ApplicationStoreContext);
+ if (context == null || context.applicationStore == null) {
+ throw new Error("Requires application store provider!")
+ }
+ return context.applicationStore
+};
+
+export const useSelectApplicationState = <TProp extends unknown >( selector: (state: IApplicationStoreState) => TProp, eqFunc = (a: TProp, b: TProp) => a === b ): TProp => {
+ const context = useContext(ApplicationStoreContext);
+ if (context == null || context.applicationStore == null) {
+ throw new Error("Requires application store provider!")
+ }
+
+ const [propState, setPropState] = useState<TProp>(selector(context.applicationStore.state));
+
+ const selectorRef = useRef(selector);
+ selectorRef.current = selector;
+
+ const propStateRef = useRef({propState});
+ propStateRef.current.propState = propState;
+
+ useEffect(() => {
+ if (context == null || context.applicationStore == null) {
+ throw new Error("Requires application store provider!")
+ }
+
+ const changedHandler = () => {
+ const newState = selectorRef.current(context.applicationStore.state);
+ if (!eqFunc(newState, propStateRef.current.propState)) {
+ setPropState(newState);
+ }
+ };
+
+ if (LogLevel > 3) {
+ console.log("useSelectApplicationState: adding handler", changedHandler);
+ }
+
+ context.applicationStore.changed.addHandler(changedHandler);
+
+ return () => {
+ if (LogLevel > 3) {
+ console.log("useSelectApplicationState: removing handler", changedHandler);
+ }
+
+ context.applicationStore.changed.removeHandler(changedHandler);
+ }
+ }, [context]);
+
+ return propState;
+
+};
+
+export const useApplicationDispatch = (): Dispatch => {
+ const context = useContext(ApplicationStoreContext);
+ if (context == null || context.applicationStore == null) {
+ throw new Error("Requires application store provider!")
+ }
+ return context.applicationStore.dispatch;
+};
diff --git a/sdnr/wt-odlux/odlux/framework/src/flux/middleware.ts b/sdnr/wt-odlux/odlux/framework/src/flux/middleware.ts
new file mode 100644
index 000000000..de6505c4f
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/flux/middleware.ts
@@ -0,0 +1,107 @@
+/**
+ * ============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, IActionHandler } from './action';
+import { Store, Dispatch, Enhancer } from './store';
+
+export interface MiddlewareArg<T> {
+ dispatch: Dispatch;
+ getState: () => T;
+}
+
+export interface Middleware<T> {
+ (obj: MiddlewareArg<T>): Function;
+}
+
+class InitialisationAction extends Action { };
+const initialisationAction = new InitialisationAction();
+
+export type ActionHandlerMapObject<S extends { [key: string]: any }, A extends Action = Action> = {
+ [K in keyof S]: IActionHandler<S[K], A>
+}
+
+export const combineActionHandler = <TState extends { [key: string]: any }, TAction extends Action = Action>(actionHandlers: ActionHandlerMapObject<TState, TAction>) : IActionHandler<TState, TAction> => {
+ const finalActionHandlers = {} as { [key: string]: any }; // https://github.com/microsoft/TypeScript/issues/31808
+ Object.keys(actionHandlers).forEach(actionHandlerKey => {
+ const handler = actionHandlers[actionHandlerKey];
+ if (typeof handler === 'function') {
+ finalActionHandlers[actionHandlerKey] = handler;
+ }
+ });
+
+ // ensure initialisation
+ Object.keys(finalActionHandlers).forEach(key => {
+ const actionHandler = finalActionHandlers[key];
+ const initialState = actionHandler(undefined, initialisationAction);
+ if (typeof initialState === 'undefined') {
+ const errorMessage = `Action handler ${ key } returned undefiend during initialization.`;
+ throw new Error(errorMessage);
+ }
+ });
+
+ return function combination<TAction extends Action>(state: TState = ({} as TState), action: TAction) {
+ let hasChanged = false;
+ const nextState = {} as { [key: string]: any }; // https://github.com/microsoft/TypeScript/issues/31808
+ Object.keys(finalActionHandlers).forEach(key => {
+ const actionHandler = finalActionHandlers[key];
+ const previousState = state[key];
+ const nextStateKey = actionHandler(previousState, action);
+ if (typeof nextStateKey === 'undefined') {
+ const errorMessage = `Given ${ action.constructor } and action handler ${ key } returned undefiend.`;
+ throw new Error(errorMessage);
+ }
+ nextState[key] = nextStateKey;
+ hasChanged = hasChanged || nextStateKey !== previousState;
+ });
+ return (hasChanged ? nextState : state) as TState;
+ };
+};
+
+export const chainMiddleware = <TStoreState>(...middlewares: Middleware<TStoreState>[]): Enhancer<TStoreState> => {
+ return (store: Store<TStoreState>) => {
+ const middlewareAPI = {
+ getState() { return store.state },
+ dispatch: <TAction extends Action>(action: TAction) => store.dispatch(action) // we want to use the combinded dispatch
+ // we should NOT use the flux dispatcher here, since the action would affect ALL stores
+ };
+ const chain = middlewares.map(middleware => middleware(middlewareAPI));
+ return compose(...chain)(store.dispatch) as Dispatch;
+ }
+};
+
+/**
+ * Composes single-argument functions from right to left. The rightmost
+ * function can take multiple arguments as it provides the signature for
+ * the resulting composite function.
+ *
+ * @param {...Function} funcs The functions to compose.
+ * @returns {Function} A function obtained by composing the argument functions
+ * from right to left. For example, compose(f, g, h) is identical to doing
+ * (...args) => f(g(h(...args))).
+ */
+const compose = (...funcs: Function[]) => {
+ if (funcs.length === 0) {
+ return (arg: any) => arg
+ }
+
+ if (funcs.length === 1) {
+ return funcs[0]
+ }
+
+ return funcs.reduce((a, b) => (...args: any[]) => a(b(...args)));
+};
+
diff --git a/sdnr/wt-odlux/odlux/framework/src/flux/store.ts b/sdnr/wt-odlux/odlux/framework/src/flux/store.ts
new file mode 100644
index 000000000..347d295e0
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/flux/store.ts
@@ -0,0 +1,106 @@
+/**
+ * ============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 { Event } from "../common/event"
+
+import { Action } from './action';
+import { IActionHandler } from './action';
+
+const LogLevel = +(localStorage.getItem('log.odlux.framework.flux.store') || 0);
+
+export interface Dispatch {
+ <TAction extends Action>(action: TAction): TAction;
+}
+
+export interface Enhancer<TStoreState> {
+ (store: Store<TStoreState>): Dispatch;
+}
+
+class InitializationAction extends Action { };
+const initializationAction = new InitializationAction();
+
+export class Store<TStoreState> {
+
+ constructor(actionHandler: IActionHandler<TStoreState>, enhancer?: Enhancer<TStoreState>)
+ constructor(actionHandler: IActionHandler<TStoreState>, initialState: TStoreState, enhancer?: Enhancer<TStoreState>)
+ constructor(actionHandler: IActionHandler<TStoreState>, initialState?: TStoreState | Enhancer<TStoreState>, enhancer?: Enhancer<TStoreState>) {
+ if (typeof initialState === 'function') {
+ enhancer = initialState as Enhancer<TStoreState>;
+ initialState = undefined;
+ }
+
+ this._isDispatching = false;
+
+ this.changed = new Event<void>();
+
+ this._actionHandler = actionHandler;
+
+ this._state = initialState as TStoreState;
+ if (enhancer) this._dispatch = enhancer(this);
+
+ this._dispatch(initializationAction);
+ }
+
+ public changed: Event<void>;
+
+ private _dispatch: Dispatch = <TAction extends Action>(payload: TAction): TAction => {
+ if (LogLevel > 2) {
+ console.log('Store::Dispatch - ', payload);
+ }
+ if (payload == null || !(payload instanceof Action)) {
+ throw new Error(
+ 'Actions must inherit from type Action. ' +
+ 'Use a custom middleware for async actions.'
+ );
+ }
+
+ if (this._isDispatching) {
+ throw new Error('ActionHandler may not dispatch actions.');
+ }
+
+ const oldState = this._state;
+ try {
+ this._isDispatching = true;
+ this._state = this._actionHandler(oldState, payload);
+ } finally {
+ this._isDispatching = false;
+ }
+
+ if (this._state !== oldState) {
+ if (LogLevel > 3) {
+ console.log('Store::Dispatch - state has changed', this._state);
+ }
+ this.changed.invoke();
+ }
+
+ return payload;
+ }
+
+ public get dispatch(): Dispatch {
+ return this._dispatch;
+ }
+
+ public get state() {
+ return this._state
+ }
+
+ private _state: TStoreState;
+ private _isDispatching: boolean;
+ private _actionHandler: IActionHandler<TStoreState>;
+
+}
+
diff --git a/sdnr/wt-odlux/odlux/framework/src/handlers/applicationRegistryHandler.ts b/sdnr/wt-odlux/odlux/framework/src/handlers/applicationRegistryHandler.ts
new file mode 100644
index 000000000..ec7b0a04a
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/handlers/applicationRegistryHandler.ts
@@ -0,0 +1,31 @@
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ */
+import { IActionHandler } from '../flux/action';
+
+import { ApplicationInfo } from '../models/applicationInfo';
+import { applicationManager } from '../services/applicationManager';
+
+export interface IApplicationRegistration {
+ [name: string]: ApplicationInfo;
+}
+
+const applicationRegistrationInit: IApplicationRegistration = applicationManager.applications;
+
+export const applicationRegistryHandler: IActionHandler<IApplicationRegistration> = (state = applicationRegistrationInit, action) => {
+ return state;
+};
diff --git a/sdnr/wt-odlux/odlux/framework/src/handlers/applicationStateHandler.ts b/sdnr/wt-odlux/odlux/framework/src/handlers/applicationStateHandler.ts
new file mode 100644
index 000000000..d0a07248d
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/handlers/applicationStateHandler.ts
@@ -0,0 +1,176 @@
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ */
+import { IActionHandler } from '../flux/action';
+
+import { SetTitleAction } from '../actions/titleActions';
+import { SetExternalLoginProviderAction } from '../actions/loginProvider';
+import { AddSnackbarNotification, RemoveSnackbarNotification } from '../actions/snackbarActions';
+import { AddErrorInfoAction, RemoveErrorInfoAction, ClearErrorInfoAction } from '../actions/errorActions';
+import { MenuAction, MenuClosedByUser } from '../actions/menuAction'
+import { SetWebsocketAction } from '../actions/websocketAction';
+
+import { IconType } from '../models/iconDefinition';
+import { ErrorInfo } from '../models/errorInfo';
+import { SnackbarItem } from '../models/snackbarItem';
+import { ExternalLoginProvider } from '../models/externalLoginProvider';
+import { ApplicationConfig } from '../models/applicationConfig';
+import { IConnectAppStoreState } from '../../../apps/connectApp/src/handlers/connectAppRootHandler';
+import { IFaultAppStoreState } from '../../../apps/faultApp/src/handlers/faultAppRootHandler';
+import { GeneralSettings, Settings } from '../models/settings';
+import { LoadSettingsAction, SetGeneralSettingsAction, SetTableSettings, SettingsDoneLoadingAction } from '../actions/settingsAction';
+import { startWebsocketSession, suspendWebsocketSession } from '../services/notificationService';
+
+
+declare module '../store/applicationStore' {
+ interface IApplicationStoreState {
+ connect: IConnectAppStoreState;
+ fault: IFaultAppStoreState
+ }
+}
+export interface IApplicationState {
+ title: string;
+ appId?: string;
+ icon?: IconType;
+ isMenuOpen: boolean;
+ isMenuClosedByUser: boolean;
+ errors: ErrorInfo[];
+ snackBars: SnackbarItem[];
+ isWebsocketAvailable: boolean | null;
+ externalLoginProviders: ExternalLoginProvider[] | null;
+ authentication: "basic"|"oauth", // basic
+ enablePolicy: boolean, // false
+ transportpceUrl : string,
+ settings: Settings & { isInitialLoadDone: boolean }
+}
+
+const applicationStateInit: IApplicationState = {
+ title: "Loading ...",
+ errors: [],
+ snackBars: [],
+ isMenuOpen: true,
+ isMenuClosedByUser: false,
+ isWebsocketAvailable: null,
+ externalLoginProviders: null,
+ authentication: "basic",
+ enablePolicy: false,
+ transportpceUrl: "",
+ settings:{
+ general: { areNotificationsEnabled: null },
+ tables: {},
+ isInitialLoadDone: false
+ }
+};
+
+export const configureApplication = (config: ApplicationConfig) => {
+ applicationStateInit.authentication = config.authentication === "oauth" ? "oauth" : "basic";
+ applicationStateInit.enablePolicy = config.enablePolicy ? true : false;
+ applicationStateInit.transportpceUrl=config.transportpceUrl == undefined ? "" : config.transportpceUrl;
+}
+
+export const applicationStateHandler: IActionHandler<IApplicationState> = (state = applicationStateInit, action) => {
+ if (action instanceof SetTitleAction) {
+ state = {
+ ...state,
+ title: action.title,
+ icon: action.icon,
+ appId: action.appId,
+ };
+ } else if (action instanceof AddErrorInfoAction) {
+ state = {
+ ...state,
+ errors: [
+ ...state.errors,
+ action.errorInfo,
+ ]
+ };
+ } else if (action instanceof RemoveErrorInfoAction) {
+ const index = state.errors.indexOf(action.errorInfo);
+ if (index > -1) {
+ state = {
+ ...state,
+ errors: [
+ ...state.errors.slice(0, index),
+ ...state.errors.slice(index + 1),
+ ]
+ };
+ }
+ } else if (action instanceof ClearErrorInfoAction) {
+ if (state.errors && state.errors.length) {
+ state = {
+ ...state,
+ errors: [],
+ };
+ }
+ } else if (action instanceof AddSnackbarNotification) {
+ state = {
+ ...state,
+ snackBars: [
+ ...state.snackBars,
+ action.notification,
+ ]
+ };
+ } else if (action instanceof RemoveSnackbarNotification) {
+ state = {
+ ...state,
+ snackBars: state.snackBars.filter(s => s.key !== action.key),
+ };
+ } else if (action instanceof MenuAction) {
+ state = {
+ ...state,
+ isMenuOpen: action.isOpen,
+ }
+ } else if (action instanceof MenuClosedByUser) {
+ state = {
+ ...state,
+ isMenuClosedByUser: action.isClosed,
+ }
+ }
+ else if (action instanceof SetWebsocketAction) {
+ state = {
+ ...state,
+ isWebsocketAvailable: action.isConnected,
+ }
+ } else if (action instanceof SetExternalLoginProviderAction){
+ state = {
+ ...state,
+ externalLoginProviders: action.externalLoginProvders,
+ }
+ }else if(action instanceof SetGeneralSettingsAction){
+
+ state = {
+ ...state,
+ settings:{tables: state.settings.tables, isInitialLoadDone:state.settings.isInitialLoadDone, general:{areNotificationsEnabled: action.areNoticationsActive}}
+ }
+ }
+ else if(action instanceof SetTableSettings){
+
+ let tableUpdate = state.settings.tables;
+ tableUpdate[action.tableName] = {columns: action.updatedColumns};
+
+ state = {...state, settings:{general: state.settings.general, isInitialLoadDone:state.settings.isInitialLoadDone, tables: tableUpdate}}
+
+ }else if(action instanceof LoadSettingsAction){
+
+ state = {...state, settings:action.settings}
+ }
+ else if(action instanceof SettingsDoneLoadingAction){
+ state= {...state, settings: {...state.settings, isInitialLoadDone: true}}
+ }
+
+ return state;
+};
diff --git a/sdnr/wt-odlux/odlux/framework/src/handlers/authenticationHandler.ts b/sdnr/wt-odlux/odlux/framework/src/handlers/authenticationHandler.ts
new file mode 100644
index 000000000..3a4891c98
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/handlers/authenticationHandler.ts
@@ -0,0 +1,59 @@
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ */
+import { IActionHandler } from '../flux/action';
+import { UpdatePolicies, UpdateUser } from '../actions/authentication';
+
+import { AuthPolicy, User } from '../models/authentication';
+
+import { onLogin, onLogout } from '../services/applicationApi';
+import { startWebsocketSession, endWebsocketSession } from '../services/notificationService';
+import { startUserSession, endUserSession } from '../services/userSessionService';
+import { getUserData } from '../services/userdataService';
+
+export interface IAuthenticationState {
+ user?: User;
+ policies?: AuthPolicy[];
+}
+
+const authenticationStateInit: IAuthenticationState = {
+ user: undefined
+};
+
+export const authenticationStateHandler: IActionHandler<IAuthenticationState> = (state = authenticationStateInit, action) => {
+ if (action instanceof UpdateUser) {
+ const {user} = action;
+
+ if (user) {
+ onLogin();
+ } else {
+ onLogout();
+ }
+
+ state = {
+ ...state,
+ user,
+ };
+ } else if (action instanceof UpdatePolicies) {
+ state = {
+ ...state,
+ policies: action.authPolicies,
+ };
+ }
+ return state;
+};
+
diff --git a/sdnr/wt-odlux/odlux/framework/src/handlers/navigationStateHandler.ts b/sdnr/wt-odlux/odlux/framework/src/handlers/navigationStateHandler.ts
new file mode 100644
index 000000000..6ecdf2f79
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/handlers/navigationStateHandler.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==========================================================================
+ */
+import { IActionHandler } from '../flux/action';
+import { LocationChanged } from '../actions/navigationActions';
+
+
+export interface INavigationState {
+ pathname: string;
+ search: string;
+ hash: string;
+}
+
+const navigationStateInit: INavigationState = {
+ pathname: '/',
+ search: '',
+ hash: '',
+};
+
+
+export const navigationStateHandler: IActionHandler<INavigationState> = (state = navigationStateInit, action) => {
+ if (action instanceof LocationChanged) {
+ state = {
+ ...state,
+ pathname: action.pathname,
+ search: action.search,
+ hash: action.hash
+ }
+ }
+ return state;
+} \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/index.dev.html b/sdnr/wt-odlux/odlux/framework/src/index.dev.html
new file mode 100644
index 000000000..69c5f0607
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/index.dev.html
@@ -0,0 +1,35 @@
+<!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>O D L UX</title>
+</head>
+
+<body>
+ <div id="app"></div>
+ <script type="text/javascript" src="./require.js"></script>
+ <script type="text/javascript" src="./config.js"></script>
+ <script >
+ // window._odluxExtraLinks = [
+ // ["imprint","https://some-url-to-imprint"],
+ // ["privacy declaration", "https://some-url-to-privacy-declaration"]
+ // ]
+ </script>
+ <script>
+ // run the application
+ require(["app" ,"connectApp","faultApp"/*,"inventoryApp","helpApp"*/], function (app,connectApp,faultApp,/*inventoryApp,helpApp*/) {
+ connectApp.register();
+ faultApp.register();
+ // inventoryApp.register();
+ // helpApp.register();
+ app("./app.tsx").configureApplication({ authentication:"basic", enablePolicy: false, transportpceUrl:"http://test.de"});
+ app("./app.tsx").runApplication();
+ });
+ </script>
+</body>
+
+</html> \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/index.html b/sdnr/wt-odlux/odlux/framework/src/index.html
new file mode 100644
index 000000000..7196b5cfe
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/index.html
@@ -0,0 +1,27 @@
+<!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>O D L UX</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(["run"], function (run) {
+ run.configureApplication({ authentication:"basic", enablePolicy: false,});
+ run.runApplication();
+ });
+
+ // don't change anything in here!!
+ </script>
+</body>
+
+</html> \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/middleware/api.ts b/sdnr/wt-odlux/odlux/framework/src/middleware/api.ts
new file mode 100644
index 000000000..8e9bc6459
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/middleware/api.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 { Action, IActionHandler } from '../flux/action';
+import { MiddlewareArg } from '../flux/middleware';
+import { Dispatch } from '../flux/store';
+
+import { IApplicationStoreState } from '../store/applicationStore';
+import { AddErrorInfoAction, ErrorInfo } from '../actions/errorActions';
+
+const baseUrl = `${ window.location.origin }${ window.location.pathname }`;
+
+export class ApiAction<TResult, TSuccessAction extends Action & { result: TResult }> extends Action {
+ constructor(public endpoint: string, public successAction: { new(result: TResult): TSuccessAction }, public authenticate: boolean = false) {
+ super();
+ }
+}
+
+export const apiMiddleware = (store: MiddlewareArg<IApplicationStoreState>) => (next: Dispatch) => <A extends Action>(action: A) => {
+
+ // So the middleware doesn't get applied to every single action
+ if (action instanceof ApiAction) {
+ const user = store && store.getState().framework.authenticationState.user;
+ const token = user && user.token || null;
+ let config = { headers: {} };
+
+ if (action.authenticate) {
+ if (token) {
+ config = {
+ ...config,
+ headers: {
+ ...config.headers,
+ // 'Authorization': `Bearer ${ token }`
+ authorization: "Basic YWRtaW46YWRtaW4="
+ }
+ }
+ } else {
+ return next(new AddErrorInfoAction({ message: 'Please login to continue.' }));
+ }
+ }
+
+ fetch(baseUrl + action.endpoint.replace(/\/{2,}/, '/'), config)
+ .then(response =>
+ response.json().then(data => ({ data, response }))
+ )
+ .then(result => {
+ next(new action.successAction(result.data));
+ })
+ .catch((error: any) => {
+ next(new AddErrorInfoAction((error instanceof Error) ? { error: error } : { message: error.toString() }));
+ });
+ }
+
+ // let all actions pass
+ return next(action);
+}
+
+export default apiMiddleware; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/middleware/logger.ts b/sdnr/wt-odlux/odlux/framework/src/middleware/logger.ts
new file mode 100644
index 000000000..fb0874f3e
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/middleware/logger.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 { Dispatch } from '../flux/store';
+import { MiddlewareApi } from '../store/applicationStore';
+
+const LogLevel = +(localStorage.getItem('log.odlux.framework.middleware.logger') || 0);
+
+function createLoggerMiddleware() {
+ return function logger({ getState }: MiddlewareApi) {
+ return (next: Dispatch): Dispatch => action => {
+ if (LogLevel > 2) console.log('will dispatch', action);
+ const returnValue = next(action);
+ if (LogLevel > 2) console.log('state after dispatch', getState());
+ return returnValue;
+ };
+ };
+}
+
+export const logger = createLoggerMiddleware();
+export default logger; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/middleware/navigation.ts b/sdnr/wt-odlux/odlux/framework/src/middleware/navigation.ts
new file mode 100644
index 000000000..14ff09f77
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/middleware/navigation.ts
@@ -0,0 +1,96 @@
+/**
+ * ============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 jwt from 'jsonwebtoken';
+import { History, createHashHistory } from 'history';
+
+import { User } from '../models/authentication';
+
+import { LocationChanged, NavigateToApplication } from '../actions/navigationActions';
+import { PushAction, ReplaceAction, GoAction, GoBackAction, GoForwardeAction } from '../actions/navigationActions';
+
+import { applicationManager } from '../services/applicationManager';
+import { loginUserAction, logoutUser } from '../actions/authentication';
+
+import { ApplicationStore } from '../store/applicationStore';
+import { Dispatch } from '../flux/store';
+
+export const history = createHashHistory();
+let applicationStore: ApplicationStore | null = null;
+
+const routerMiddlewareCreator = (historyParam: History) => () => (next: Dispatch): Dispatch => (action) => {
+
+ if (action instanceof NavigateToApplication) {
+ const application = applicationManager.applications && applicationManager.applications[action.applicationName];
+ if (application) {
+ const href = `/${application.path || application.name}${action.href ? '/' + action.href : ''}`.replace(/\/{2,}/i, '/');
+ if (action.replace) {
+ historyParam.replace(href, action.state);
+ } else {
+ historyParam.push(href, action.state);
+ }
+ }
+ } else if (action instanceof PushAction) {
+ historyParam.push(action.href, action.state);
+ } else if (action instanceof ReplaceAction) {
+ historyParam.replace(action.href, action.state);
+ } else if (action instanceof GoAction) {
+ historyParam.go(action.index);
+ } else if (action instanceof GoBackAction) {
+ historyParam.goBack();
+ } else if (action instanceof GoForwardeAction) {
+ historyParam.goForward();
+ } else if (action instanceof LocationChanged) {
+ // ensure user is logged in and token is valid
+ if (action.pathname.startsWith('/oauth') && (action.search.startsWith('?token='))) {
+ const ind = action.search.lastIndexOf('token=');
+ const tokenStr = ind > -1 ? action.search.substring(ind + 6) : null;
+ const token = tokenStr && jwt.decode(tokenStr);
+ if (tokenStr && token) {
+ // @ts-ignore
+ const user = new User({ username: token.name, access_token: tokenStr, token_type: 'Bearer', expires: token.exp, issued: token.iat }) || undefined;
+ applicationStore?.dispatch(loginUserAction(user));
+ }
+ } if (!action.pathname.startsWith('/login') && applicationStore && (!applicationStore.state.framework.authenticationState.user || !applicationStore.state.framework.authenticationState.user.isValid)) {
+ historyParam.replace(`/login?returnTo=${action.pathname}`);
+ applicationStore.dispatch(logoutUser());
+
+ } else if (action.pathname.startsWith('/login') && applicationStore && (applicationStore.state.framework.authenticationState.user && applicationStore.state.framework.authenticationState.user.isValid)) {
+ historyParam.replace('/');
+ } else {
+ return next(action);
+ }
+ } else {
+ return next(action);
+ }
+ return action;
+};
+
+const startListener = (historyParam: History, store: ApplicationStore) => {
+ store.dispatch(new LocationChanged(historyParam.location.pathname, historyParam.location.search, historyParam.location.hash));
+ historyParam.listen((location) => {
+ store.dispatch(new LocationChanged(location.pathname, location.search, location.hash));
+ });
+};
+
+export const startHistoryListener = (store: ApplicationStore) => {
+ applicationStore = store;
+ startListener(history, store);
+};
+
+export const routerMiddleware = routerMiddlewareCreator(history);
+export default routerMiddleware;
diff --git a/sdnr/wt-odlux/odlux/framework/src/middleware/policies.ts b/sdnr/wt-odlux/odlux/framework/src/middleware/policies.ts
new file mode 100644
index 000000000..662ecddb3
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/middleware/policies.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==========================================================================
+ */
+
+import authenticationService from '../services/authenticationService';
+
+import { UpdateUser, UpdatePolicies } from '../actions/authentication';
+import { Dispatch } from '../flux/store';
+import { MiddlewareApi } from '../store/applicationStore';
+
+function updatePoliciesMiddleware() {
+ return ({ dispatch, getState }: MiddlewareApi) =>
+ (next : Dispatch) : Dispatch =>
+ action => {
+ const { framework: { applicationState: { enablePolicy } } } = getState() || { framework: { applicationState: { } } };
+ if (enablePolicy && action instanceof UpdateUser) {
+ next(action);
+ authenticationService.getAccessPolicies().then((policies) => dispatch(new UpdatePolicies(policies||undefined)));
+ return action;
+ }
+ if (enablePolicy === false) next(new UpdatePolicies());
+ return next(action);
+ };
+}
+
+export const updatePolicies = updatePoliciesMiddleware();
+export default updatePolicies; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/middleware/thunk.ts b/sdnr/wt-odlux/odlux/framework/src/middleware/thunk.ts
new file mode 100644
index 000000000..18f40c4ca
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/middleware/thunk.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 { Dispatch } from '../flux/store';
+import { MiddlewareApi } from '../store/applicationStore';
+
+function createThunkMiddleware() {
+ return ({ dispatch, getState }: MiddlewareApi) =>
+ (next : Dispatch) : Dispatch =>
+ action => {
+ if (typeof action === 'function') {
+ return action(dispatch, getState);
+ }
+
+ return next(action);
+ };
+}
+
+export const thunk = createThunkMiddleware();
+export default thunk; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/models/applicationConfig.ts b/sdnr/wt-odlux/odlux/framework/src/models/applicationConfig.ts
new file mode 100644
index 000000000..0ae9c266b
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/models/applicationConfig.ts
@@ -0,0 +1,5 @@
+export type ApplicationConfig = {
+ authentication: "basic"|"oauth", // basic
+ enablePolicy: false, // false
+ transportpceUrl? : string
+}; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/models/applicationInfo.ts b/sdnr/wt-odlux/odlux/framework/src/models/applicationInfo.ts
new file mode 100644
index 000000000..ff07b7d7b
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/models/applicationInfo.ts
@@ -0,0 +1,55 @@
+/**
+ * ============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 { ComponentType } from 'react';
+import { IconType } from './iconDefinition';
+
+import { IActionHandler } from '../flux/action';
+import { Middleware } from '../flux/middleware';
+import { SettingsComponentProps } from './settings';
+
+/** Represents the information needed about an application to integrate. */
+export class ApplicationInfo {
+ /** The name of the application. */
+ name: string;
+ /** Optional: The title of the application, if null ot undefined the name will be used. */
+ title?: string;
+ /** Optional: The icon of the application for the navigation and title bar. */
+ icon?: IconType;
+ /** Optional: The description of the application. */
+ description?: string;
+ /** The root component of the application. */
+ rootComponent: ComponentType;
+ /** Optional: The root action handler of the application. */
+ rootActionHandler?: IActionHandler<{ [key: string]: any }>;
+ /** Optional: Application speciffic middlewares. */
+ middlewares?: Middleware<{ [key: string]: any }>[];
+ /** Optional: A mapping object with the exported components. */
+ exportedComponents?: { [key: string]: ComponentType }
+ /** Optional: The entry to be shown in the menu. If undefiened the name will be used. */
+ menuEntry?: string | React.ComponentType;
+ /** Optional: A component to be shown in the menu when this app is active below the main entry. If undefiened the name will be used. */
+ subMenuEntry?: React.ComponentType;
+ /** Optional: A component to be shown in the applications status bar. If undefiened the name will be used. */
+ statusBarElement?: React.ComponentType;
+ /** Optional: A component to be shown in the dashboardview. If undefiened the name will be used. */
+ dashbaordElement?: React.ComponentType;
+ /** Optional: A component shown in the settings view */
+ settingsElement?: React.ComponentType<SettingsComponentProps>;
+ /** Optional: The pasth for this application. If undefined the name will be use as path. */
+ path?: string;
+}
diff --git a/sdnr/wt-odlux/odlux/framework/src/models/authentication.ts b/sdnr/wt-odlux/odlux/framework/src/models/authentication.ts
new file mode 100644
index 000000000..f56538184
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/models/authentication.ts
@@ -0,0 +1,94 @@
+/**
+ * ============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 type AuthToken = {
+ username: string;
+ access_token: string;
+ token_type: string;
+ /***
+ * datetime the token should expire in unix timestamp
+ *
+ * must be in seconds
+ */
+ expires: number;
+ /***
+ * time the token was issued in unix timestamp
+ *
+ * must be in seconds
+ *
+ */
+ issued: number;
+}
+
+export type AuthPolicy = {
+ path: string;
+ methods: {
+ get?: boolean;
+ post?: boolean;
+ put?: boolean;
+ patch?: boolean;
+ delete?: boolean;
+ }
+}
+
+export class User {
+
+ constructor (private _bearerToken: AuthToken) {
+
+ }
+
+ public get user(): string | null {
+ return this._bearerToken && this._bearerToken.username;
+ };
+
+ public get token(): string | null {
+ return this._bearerToken && this._bearerToken.access_token;
+ }
+
+ public get tokenType(): string | null {
+ return this._bearerToken && this._bearerToken.token_type;
+ }
+
+ /***
+ * Time the user should be logged out, in unix timestamp in seconds
+ */
+ public get logoutAt(): number{
+ return this._bearerToken && this._bearerToken.expires;
+ }
+
+ /***
+ * Time the user logged in, in unix timestamp in seconds
+ */
+ public get loginAt(): number{
+ return this._bearerToken && this._bearerToken.issued;
+ }
+
+ public get isValid(): boolean {
+ return (this._bearerToken && (new Date().valueOf()) < this._bearerToken.expires*1000) || false;
+ }
+
+ public toString() {
+ return JSON.stringify(this._bearerToken);
+ }
+
+ public static fromString(data: string) {
+ return new User(JSON.parse(data));
+ }
+
+
+}
diff --git a/sdnr/wt-odlux/odlux/framework/src/models/elasticSearch.ts b/sdnr/wt-odlux/odlux/framework/src/models/elasticSearch.ts
new file mode 100644
index 000000000..fc4383612
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/models/elasticSearch.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==========================================================================
+ */
+export type Result<TSource extends {}> = {
+ "data-provider:output": {
+ pagination?: {
+ size: number;
+ page: number;
+ total: number;
+ },
+ data: TSource[];
+ }
+}
+
+export type SingeResult<TSource extends {}> = {
+ "data-provider:output": TSource;
+}
+
+
+export type ResultTopology<TSource extends {}> = {
+ "output": {
+ pagination?: {
+ size: number;
+ page: number;
+ total: number;
+ },
+ data: TSource[];
+ }
+}
+
+export type HitEntry<TSource extends {}> = {
+ _index: string;
+ _type: string;
+ _id: string;
+ _score: number;
+ _source: TSource;
+}
+
+type ActionResponse ={
+ _index: string;
+ _type: string;
+ _id: string;
+ _shards: {
+ total: number,
+ successful: number,
+ failed: number
+ },
+
+}
+
+export type PostResponse = ActionResponse & {
+ created: boolean
+}
+
+export type DeleteResponse = ActionResponse & {
+ found: boolean
+}
+
diff --git a/sdnr/wt-odlux/odlux/framework/src/models/errorInfo.ts b/sdnr/wt-odlux/odlux/framework/src/models/errorInfo.ts
new file mode 100644
index 000000000..21217a108
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/models/errorInfo.ts
@@ -0,0 +1,29 @@
+/**
+ * ============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 type ErrorInfo = {
+ title?: string,
+ error?: Error | null,
+ url?: string,
+ line?: number,
+ col?: number,
+ info?: {
+ extra?: string,
+ componentStack?: string
+ },
+ message?: string
+} \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/models/externalLoginProvider.ts b/sdnr/wt-odlux/odlux/framework/src/models/externalLoginProvider.ts
new file mode 100644
index 000000000..357feed06
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/models/externalLoginProvider.ts
@@ -0,0 +1,23 @@
+/**
+ * ============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 type ExternalLoginProvider = {
+ id: string;
+ title: string;
+ loginUrl: string;
+ } \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/models/iconDefinition.ts b/sdnr/wt-odlux/odlux/framework/src/models/iconDefinition.ts
new file mode 100644
index 000000000..ff50aa73c
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/models/iconDefinition.ts
@@ -0,0 +1,21 @@
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ */
+
+import { IconDefinition } from '@fortawesome/free-solid-svg-icons';
+
+export type IconType = IconDefinition | string; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/models/index.ts b/sdnr/wt-odlux/odlux/framework/src/models/index.ts
new file mode 100644
index 000000000..4b8dc20aa
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/models/index.ts
@@ -0,0 +1,18 @@
+/**
+ * ============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 * from './elasticSearch'; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/models/restService.ts b/sdnr/wt-odlux/odlux/framework/src/models/restService.ts
new file mode 100644
index 000000000..03f580b01
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/models/restService.ts
@@ -0,0 +1,48 @@
+/**
+ * ============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==========================================================================
+ */
+
+/**
+ * The PlainObject type is a JavaScript object containing zero or more key-value pairs.
+ */
+export interface PlainObject<T = any> {
+ [key: string]: T;
+}
+
+export interface AjaxParameter {
+ /**
+ * The HTTP method to use for the request (e.g. "POST", "GET", "PUT").
+ */
+ method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'OPTIONS' | 'PATCH';
+ /**
+ * An object of additional header key/value pairs to send along with requests using the XMLHttpRequest
+ * transport. The header X-Requested-With: XMLHttpRequest is always added, but its default
+ * XMLHttpRequest value can be changed here. Values in the headers setting can also be overwritten from
+ * within the beforeSend function.
+ */
+ headers?: PlainObject<string | null | undefined>;
+ /**
+ * Data to be sent to the server. It is converted to a query string, if not already a string. It's
+ * appended to the url for GET-requests. See processData option to prevent this automatic processing.
+ * Object must be Key/Value pairs. If value is an Array, jQuery serializes multiple values with same
+ * key based on the value of the traditional setting (described below).
+ */
+ data?: PlainObject | string;
+}
+
+
+
diff --git a/sdnr/wt-odlux/odlux/framework/src/models/settings.ts b/sdnr/wt-odlux/odlux/framework/src/models/settings.ts
new file mode 100644
index 000000000..1752d6a52
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/models/settings.ts
@@ -0,0 +1,48 @@
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ */
+
+export type TableSettingsColumn = {
+ property: string;
+ displayed: boolean;
+};
+
+export type TableSettings = {
+ tables:{
+ [key: string]: {
+ columns: TableSettingsColumn[];
+
+ //match prop names, hide them
+ //via property name! -> only those which are hidden!
+ //all others default false, oh yeah
+ //or maybe the other way around, gotta think about that
+
+ };
+ };
+};
+
+export type GeneralSettings = {
+ general:{
+ areNotificationsEnabled: boolean | null;
+ };
+};
+
+export type Settings = TableSettings & GeneralSettings;
+
+export type SettingsComponentProps = {
+ onClose(): void;
+}; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/models/snackbarItem.ts b/sdnr/wt-odlux/odlux/framework/src/models/snackbarItem.ts
new file mode 100644
index 000000000..c10d7ef37
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/models/snackbarItem.ts
@@ -0,0 +1,20 @@
+/**
+ * ============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 { OptionsObject } from "notistack";
+
+export type SnackbarItem = { key: number, message: string, options?: OptionsObject }; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/run.ts b/sdnr/wt-odlux/odlux/framework/src/run.ts
new file mode 100644
index 000000000..991b7494e
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/run.ts
@@ -0,0 +1,19 @@
+/**
+ * ============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 { configureApplication } from './handlers/applicationStateHandler';
+export { runApplication } from './app';
diff --git a/sdnr/wt-odlux/odlux/framework/src/services/applicationApi.ts b/sdnr/wt-odlux/odlux/framework/src/services/applicationApi.ts
new file mode 100644
index 000000000..faa998450
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/services/applicationApi.ts
@@ -0,0 +1,74 @@
+/**
+ * ============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 { Event } from '../common/event';
+import { ApplicationStore } from '../store/applicationStore';
+import { AuthMessage, sendMessage } from './broadcastService';
+
+let resolveApplicationStoreInitialized: (store: ApplicationStore) => void;
+let applicationStore: ApplicationStore | null = null;
+const applicationStoreInitialized: Promise<ApplicationStore> = new Promise((resolve) => resolveApplicationStoreInitialized = resolve);
+
+const loginEvent = new Event();
+const logoutEvent = new Event();
+
+const authChannelName = 'odlux_auth';
+
+export const onLogin = () => {
+
+ const message : AuthMessage = { key: 'login', data: {} };
+ sendMessage(message, authChannelName);
+ loginEvent.invoke();
+
+};
+
+export const onLogout = () => {
+
+ document.cookie = 'JSESSIONID=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
+
+ const message : AuthMessage = { key: 'logout', data: {} };
+ sendMessage(message, authChannelName);
+ logoutEvent.invoke();
+};
+
+export const setApplicationStore = (store: ApplicationStore) => {
+ if (!applicationStore && store) {
+ applicationStore = store;
+ resolveApplicationStoreInitialized(store);
+ }
+};
+
+export const applicationApi = {
+ get applicationStore(): ApplicationStore | null {
+ return applicationStore;
+ },
+
+ get applicationStoreInitialized(): Promise<ApplicationStore> {
+ return applicationStoreInitialized;
+ },
+
+ get loginEvent() {
+ return loginEvent;
+ },
+
+ get logoutEvent() {
+ return logoutEvent;
+ },
+};
+
+export default applicationApi; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/services/applicationManager.ts b/sdnr/wt-odlux/odlux/framework/src/services/applicationManager.ts
new file mode 100644
index 000000000..bd620a020
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/services/applicationManager.ts
@@ -0,0 +1,53 @@
+/**
+ * ============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 { ApplicationInfo } from '../models/applicationInfo';
+import { Event } from '../common/event';
+
+import { applicationApi } from './applicationApi';
+
+/** Represents registry to manage all applications. */
+class ApplicationManager {
+
+ /** Stores all registered applications. */
+ private _applications: { [key: string]: ApplicationInfo };
+
+ /** Initializes a new instance of this class. */
+ constructor() {
+ this._applications = {};
+ this.changed = new Event<void>();
+ }
+
+ /** The changed event will fire if the registration has changed. */
+ public changed: Event<void>;
+
+ /** Registers a new application. */
+ public registerApplication(applicationInfo: ApplicationInfo) {
+ this._applications[applicationInfo.name] = applicationInfo;
+ this.changed.invoke();
+ return applicationApi;
+ }
+
+ /** Gets all registered applications. */
+ public get applications() {
+ return this._applications;
+ }
+}
+
+/** A singleton instance of the application manager. */
+export const applicationManager = new ApplicationManager();
+export default applicationManager; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/services/authenticationService.ts b/sdnr/wt-odlux/odlux/framework/src/services/authenticationService.ts
new file mode 100644
index 000000000..b9c1a5a94
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/services/authenticationService.ts
@@ -0,0 +1,96 @@
+/**
+ * ============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 { AuthPolicy, AuthToken } from "../models/authentication";
+import { ExternalLoginProvider } from "../models/externalLoginProvider";
+
+import { requestRest, formEncode, requestRestExt } from "./restService";
+
+type AuthTokenResponse = {
+ access_token: string;
+ token_type: string;
+ expires_at: number;
+ issued_at: number;
+}
+
+class AuthenticationService {
+ public async getAvaliableExteralProvider() {
+ const result = await requestRest<ExternalLoginProvider[]>(`oauth/providers`, {
+ method: "GET",
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded'
+ },
+ }, false);
+ return result;
+ }
+
+ public async authenticateUserOAuth(email: string, password: string, scope: string): Promise<AuthToken | null> {
+ const result = await requestRest<AuthTokenResponse>(`oauth/login`, {
+ method: "POST",
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded'
+ },
+ body: formEncode({
+ grant_type: "password",
+ username: email,
+ password: password,
+ scope: scope
+ })
+ }, false);
+
+
+ return result && {
+ username: email,
+ access_token: result.access_token,
+ token_type: result.token_type,
+ expires: result.expires_at,
+ issued: result.issued_at
+ } || null;
+ }
+
+ public async authenticateUserBasicAuth(email: string, password: string, scope: string): Promise<AuthToken | null> {
+ const result = await requestRest<string>(`rests/data/network-topology:network-topology/topology=topology-netconf?fields=node(node-id)`, {
+ method: "GET",
+ headers: {
+ 'Authorization': "Basic " + btoa(email + ":" + password)
+ },
+ }, false);
+
+ if (result) {
+ return {
+ username: email,
+ access_token: btoa(email + ":" + password),
+ token_type: "Basic",
+ expires: (new Date()).valueOf() / 1000 + 86400, // 1 day
+ issued: (new Date()).valueOf() / 1000
+ }
+ }
+ return null;
+ }
+
+ public async getAccessPolicies(){
+ return await requestRest<AuthPolicy[]>(`oauth/policies`, { method: "GET" }, true);
+ }
+
+ public async getServerReadyState(){
+ const result = await requestRestExt(`/ready`, { method: "GET" }, false);
+ return result.status == (200 || 304) ? true : false;
+ }
+}
+
+export const authenticationService = new AuthenticationService();
+export default authenticationService; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/services/broadcastService.ts b/sdnr/wt-odlux/odlux/framework/src/services/broadcastService.ts
new file mode 100644
index 000000000..202bf5563
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/services/broadcastService.ts
@@ -0,0 +1,115 @@
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ */
+
+import { setGeneralSettingsAction } from '../actions/settingsAction';
+import { loginUserAction, logoutUser } from '../actions/authentication';
+import { ReplaceAction } from '../actions/navigationActions';
+import { User } from '../models/authentication';
+import { ApplicationStore } from '../store/applicationStore';
+
+type Broadcaster = {
+ channel: BroadcastChannel;
+ key: String;
+};
+
+type AuthTypes = 'login' | 'logout';
+export type AuthMessage = {
+ key: AuthTypes;
+ data: any;
+};
+
+type SettingsType = 'general';
+export type SettingsMessage = {
+ key: SettingsType;
+ enableNotifications: boolean;
+ user: string;
+};
+
+const channels: Broadcaster[] = [];
+let store: ApplicationStore | null = null;
+
+export const saveChannel = (channel: BroadcastChannel, channelName: string) => {
+ channels.push({ channel: channel, key: channelName });
+};
+
+const createSettingsBroadcastChannel = () => {
+
+ const name = 'odlux_settings';
+ const bc: BroadcastChannel = new BroadcastChannel(name);
+ channels.push({ channel: bc, key: name });
+
+ bc.onmessage = (eventMessage: MessageEvent<SettingsMessage>) => {
+ console.log(eventMessage);
+
+ if (eventMessage.data.key === 'general') {
+
+ if (store?.state.framework.authenticationState.user) {
+ const data = eventMessage.data;
+ if (store.state.framework.authenticationState.user.user === data.user) {
+ store?.dispatch(setGeneralSettingsAction(data.enableNotifications));
+ }
+ }
+ }
+ };
+};
+
+const createAuthBroadcastChannel = () => {
+ const name = 'odlux_auth';
+ const bc: BroadcastChannel = new BroadcastChannel(name);
+ channels.push({ channel: bc, key: name });
+
+ bc.onmessage = (eventMessage: MessageEvent<AuthMessage>) => {
+ console.log(eventMessage);
+
+ if (eventMessage.data.key === 'login') {
+ if (!store?.state.framework.authenticationState.user) {
+ const initialToken = localStorage.getItem('userToken');
+ if (initialToken) {
+ store?.dispatch(loginUserAction(User.fromString(initialToken)));
+ store?.dispatch(new ReplaceAction('/'));
+ }
+ }
+ } else if (eventMessage.data.key === 'logout') {
+
+ if (store?.state.framework.authenticationState.user) {
+ store?.dispatch(logoutUser());
+ store?.dispatch(new ReplaceAction('/login'));
+ }
+ }
+ };
+};
+
+export const startBroadcastChannel = (applicationStore: ApplicationStore) => {
+ store = applicationStore;
+
+ // might decide to use one general broadcast channel with more keys in the future
+ createAuthBroadcastChannel();
+ createSettingsBroadcastChannel();
+};
+
+export const getBroadcastChannel = (channelName: string) => {
+ const foundChannel = channels.find(s => s.key === channelName);
+ return foundChannel?.channel;
+};
+
+export const sendMessage = (data: any, channel: string) => {
+ const foundChannel = channels.find(s => s.key === channel);
+ if (foundChannel) {
+ foundChannel.channel.postMessage(data);
+ }
+};
diff --git a/sdnr/wt-odlux/odlux/framework/src/services/index.ts b/sdnr/wt-odlux/odlux/framework/src/services/index.ts
new file mode 100644
index 000000000..2f64ba0ac
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/services/index.ts
@@ -0,0 +1,22 @@
+/**
+ * ============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 { applicationManager } from './applicationManager';
+export { subscribe, unsubscribe } from './notificationService';
+export { requestRest } from './restService';
+export { saveUserData as saveUserdata, getUserData as getUserdata } from './userdataService';
+
diff --git a/sdnr/wt-odlux/odlux/framework/src/services/notificationService.ts b/sdnr/wt-odlux/odlux/framework/src/services/notificationService.ts
new file mode 100644
index 000000000..b2880b9de
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/services/notificationService.ts
@@ -0,0 +1,220 @@
+/**
+ * ============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 { ApplicationStore } from '../store/applicationStore';
+import { SetWebsocketAction } from '../actions/websocketAction';
+
+const socketUrl = [location.protocol === 'https:' ? 'wss://' : 'ws://', location.hostname, ':', location.port, '/websocket'].join('');
+const subscriptions: { [scope: string]: SubscriptionCallback[] } = {};
+let socketReady: Promise<WebSocket>;
+let wasWebsocketConnectionEstablished: undefined | boolean;
+let applicationStore: ApplicationStore | null;
+
+let areWebsocketsStoppedViaSettings = false;
+
+
+export interface IFormatedMessage {
+ "event-time": string,
+ "data": {
+ "counter": number,
+ "attribute-name": string,
+ "time-stamp": string,
+ "object-id-ref": string,
+ "new-value": string
+ },
+ "node-id": string,
+ "type": {
+ "namespace": string,
+ "revision": string,
+ "type": string
+ }
+}
+
+export type SubscriptionCallback<TMessage extends IFormatedMessage = IFormatedMessage> = (msg: TMessage) => void;
+
+function setCurrentSubscriptions(notificationSocket: WebSocket) {
+ const scopesToSubscribe = Object.keys(subscriptions);
+ if (notificationSocket.readyState === notificationSocket.OPEN) {
+ const data = {
+ 'data': 'scopes',
+ 'scopes':[{
+ "schema":{
+ "namespace":"urn:opendaylight:params:xml:ns:yang:devicemanager",
+ "revision":"*",
+ "notification": scopesToSubscribe
+ }
+ }]
+ };
+ notificationSocket.send(JSON.stringify(data));
+ return true;
+ };
+ return false;
+}
+
+function addScope<TMessage extends IFormatedMessage = IFormatedMessage>(scope: string | string[], callback: SubscriptionCallback<TMessage>) {
+ const scopes = scope instanceof Array ? scope : [scope];
+
+ // send all new scopes to subscribe
+ const newScopesToSubscribe: string[] = scopes.reduce((acc: string[], cur: string) => {
+ const currentCallbacks = subscriptions[cur];
+ if (currentCallbacks) {
+ if (!currentCallbacks.some(c => c === callback)) {
+ currentCallbacks.push(callback);
+ }
+ } else {
+ subscriptions[cur] = [callback];
+ acc.push(cur);
+ }
+ return acc;
+ }, []);
+
+ if (newScopesToSubscribe.length === 0) {
+ return true;
+ }
+ return false;
+}
+
+function removeScope<TMessage extends IFormatedMessage = IFormatedMessage>(scope: string | string[], callback: SubscriptionCallback<TMessage>) {
+ const scopes = scope instanceof Array ? scope : [scope];
+ scopes.forEach(s => {
+ const callbacks = subscriptions[s];
+ const index = callbacks && callbacks.indexOf(callback);
+ if (index > -1) {
+ callbacks.splice(index, 1);
+ }
+ if (callbacks.length === 0) {
+ subscriptions[s] === undefined;
+ }
+ });
+}
+
+export function subscribe<TMessage extends IFormatedMessage = IFormatedMessage>(scope: string | string[], callback: SubscriptionCallback<TMessage>): Promise<boolean> {
+ addScope(scope, callback)
+ return socketReady && socketReady.then((notificationSocket) => {
+ // send a subscription to all active scopes
+ return setCurrentSubscriptions(notificationSocket);
+ }) || true;
+}
+
+export function unsubscribe<TMessage extends IFormatedMessage = IFormatedMessage>(scope: string | string[], callback: SubscriptionCallback<TMessage>): Promise<boolean> {
+ removeScope(scope, callback);
+ return socketReady && socketReady.then((notificationSocket) => {
+ // send a subscription to all active scopes
+ return setCurrentSubscriptions(notificationSocket);
+ }) || true;
+}
+
+export const startNotificationService = (store: ApplicationStore) => {
+ applicationStore = store;
+}
+
+const connect = (): Promise<WebSocket> => {
+ return new Promise((resolve, reject) => {
+ const notificationSocket = new WebSocket(socketUrl);
+
+ notificationSocket.onmessage = (event: MessageEvent<string>) => {
+ // process received event
+
+ if (event.data && typeof event.data === "string" ) {
+ const msg = JSON.parse(event.data) as IFormatedMessage;
+ const callbacks = msg?.type?.type && subscriptions[msg.type.type];
+ if (callbacks) {
+ callbacks.forEach(cb => {
+ // ensure all callbacks will be called
+ try {
+ return cb(msg);
+ } catch (reason) {
+ console.error(reason);
+ }
+ });
+ }
+ }
+
+ };
+
+ notificationSocket.onerror = function (error) {
+ console.log("Socket error:");
+ console.log(error);
+ reject("Socket error: " + error);
+ if (applicationStore) {
+ applicationStore.dispatch(new SetWebsocketAction(false));
+ }
+ };
+
+ notificationSocket.onopen = function (event) {
+ if (applicationStore) {
+ applicationStore.dispatch(new SetWebsocketAction(true));
+ }
+ console.log("Socket connection opened.");
+ resolve(notificationSocket);
+
+ // send a subscription to all active scopes
+ setCurrentSubscriptions(notificationSocket);
+ };
+
+ notificationSocket.onclose = function (event) {
+ console.log("socket connection closed");
+ dispatchSocketClose();
+
+ const isUserLoggedIn = applicationStore?.state.framework.authenticationState.user && applicationStore?.state.framework.authenticationState.user?.isValid;
+
+ if (isUserLoggedIn && !areWebsocketsStoppedViaSettings) {
+ socketReady = connect();
+ }
+ };
+ });
+}
+
+
+export const startWebsocketSession = () => {
+ socketReady = connect();
+ areWebsocketsStoppedViaSettings = false;
+}
+
+export const suspendWebsocketSession = () =>{
+ areWebsocketsStoppedViaSettings = true;
+ closeSocket();
+}
+
+export const endWebsocketSession = () => {
+ closeSocket();
+}
+
+const closeSocket = () =>{
+
+ if (socketReady) {
+ socketReady.then(websocket => {
+ websocket.close();
+ });
+ }else{
+ dispatchSocketClose();
+ }
+}
+
+const dispatchSocketClose = () =>{
+ const isUserLoggedIn = applicationStore?.state.framework.authenticationState.user && applicationStore?.state.framework.authenticationState.user?.isValid;
+
+ if(isUserLoggedIn){
+ applicationStore?.dispatch(new SetWebsocketAction(false));
+ }else{
+ applicationStore?.dispatch(new SetWebsocketAction(null));
+ }
+}
+
+
+
+
diff --git a/sdnr/wt-odlux/odlux/framework/src/services/restAccessorService.ts b/sdnr/wt-odlux/odlux/framework/src/services/restAccessorService.ts
new file mode 100644
index 000000000..5ed4d7b6e
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/services/restAccessorService.ts
@@ -0,0 +1,93 @@
+/**
+ * ============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 $ from 'jquery';
+import { Action, IActionHandler } from '../flux/action';
+import { MiddlewareArg } from '../flux/middleware';
+import { Dispatch } from '../flux/store';
+
+import { IApplicationStoreState } from '../store/applicationStore';
+import { AddErrorInfoAction, ErrorInfo } from '../actions/errorActions';
+import { PlainObject, AjaxParameter } from 'models/restService';
+
+export const absoluteUri = /^(https?:\/\/|blob:)/i;
+export const baseUrl = `${ window.location.origin }${ window.location.pathname }`;
+
+class RestBaseAction extends Action { }
+
+export const createRestApiAccessor = <TResult extends PlainObject>(urlOrPath: string, initialValue: TResult) => {
+ const isLocalRequest = !absoluteUri.test(urlOrPath);
+ const uri = isLocalRequest ? `${ baseUrl }/${ urlOrPath }`.replace(/\/{2,}/, '/') : urlOrPath ;
+
+ class RestRequestAction extends RestBaseAction { constructor(public settings?: AjaxParameter) { super(); } }
+
+ class RestResponseAction extends RestBaseAction { constructor(public result: TResult) { super(); } }
+
+ class RestErrorAction extends RestBaseAction { constructor(public error?: Error | string) { super(); } }
+
+ type RestAction = RestRequestAction | RestResponseAction | RestErrorAction;
+
+ /** Represents our middleware to handle rest backend requests */
+ const restMiddleware = (api: MiddlewareArg<IApplicationStoreState>) =>
+ (next: Dispatch) => (action: RestAction): RestAction => {
+
+ // pass all actions through by default
+ next(action);
+ // handle the RestRequestAction
+ if (action instanceof RestRequestAction) {
+ const state = api.getState();
+ const authHeader = isLocalRequest && state && state.framework.authenticationState.user && state.framework.authenticationState.user.token
+ ? { "Authentication": "Bearer " + state.framework.authenticationState.user.token } : { };
+ $.ajax({
+ url: uri,
+ method: (action.settings && action.settings.method) || "GET",
+ headers: { ...authHeader, ...(action.settings && action.settings.headers ? action.settings.headers : { }) },
+ }).then((data: TResult) => {
+ next(new RestResponseAction(data));
+ }).catch((err: any) => {
+ next(new RestErrorAction());
+ next(new AddErrorInfoAction((err instanceof Error) ? { error: err } : { message: err.toString() }));
+ });
+ }
+ // allways return action
+ return action;
+ };
+
+ /** Represents our action handler to handle our actions */
+ const restActionHandler: IActionHandler<TResult> = (state = initialValue, action) => {
+ if (action instanceof RestRequestAction) {
+ return {
+ ...(state as any),
+ busy: true
+ };
+ } else if (action instanceof RestResponseAction) {
+ return action.result;
+ } else if (action instanceof RestErrorAction) {
+ return initialValue;
+ }
+ return state;
+ };
+
+ return {
+ requestAction: RestRequestAction,
+ actionHandler: restActionHandler,
+ middleware: restMiddleware,
+ };
+}
+
+
+
diff --git a/sdnr/wt-odlux/odlux/framework/src/services/restService.ts b/sdnr/wt-odlux/odlux/framework/src/services/restService.ts
new file mode 100644
index 000000000..0be3dca07
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/services/restService.ts
@@ -0,0 +1,168 @@
+/**
+ * ============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 { ReplaceAction } from '../actions/navigationActions';
+import { AddErrorInfoAction } from '../actions/errorActions';
+
+import { storeService } from './storeService';
+
+const baseUri = `${ window.location.origin }`;
+const absUrlPattern = /^https?:\/\//;
+
+export const formEncode = (params: { [key: string]: string | number }) => Object.keys(params).map((key) => {
+ return encodeURIComponent(key) + '=' + encodeURIComponent(params[key].toString());
+}).join('&');
+
+const wildcardToRegexp = (pattern: string) => {
+ return new RegExp('^' + pattern.split(/\*\*/).map((p) => p.split(/\*+/).map((i) => i.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&')).join('^[/]')).join('.*') + '$');
+};
+
+export const getAccessPolicyByUrl = (url: string) => {
+ const result = {
+ GET : false,
+ POST: false,
+ PUT: false,
+ PATCH: false,
+ DELETE: false,
+ };
+
+ if (!storeService.applicationStore) return result;
+
+ const { state: { framework: { applicationState: { enablePolicy }, authenticationState: { policies } } } } = storeService.applicationStore!;
+
+ result.GET = true;
+ result.POST = true;
+ result.PUT = true;
+ result.PATCH = true;
+ result.DELETE = true;
+
+ if (!enablePolicy || !policies || policies.length === 0) return result;
+
+ policies.forEach(p => {
+ const re = wildcardToRegexp(p.path);
+ if (re.test(url)) {
+ result.GET = p.methods.get != null ? p.methods.get : result.GET ;
+ result.POST = p.methods.post != null ? p.methods.post : result.POST ;
+ result.PUT = p.methods.put != null ? p.methods.put : result.PUT ;
+ result.PATCH = p.methods.patch != null ? p.methods.patch : result.PATCH ;
+ result.DELETE = p.methods.delete != null ? p.methods.delete : result.DELETE ;
+ }
+ });
+
+ return result;
+
+};
+
+/** Sends a rest request to the given path and reports the server state.
+ * @returns An object with the server state, a message and the data or undefined in case of a json parse error.
+ */
+export async function requestRestExt<TData>(path: string = '', initParam: RequestInit = {}, authenticate: boolean = true, isResource: boolean = false): Promise<{ status: number; message?: string; data: TData | null | undefined }> {
+ const result: { status: number; message?: string; data: TData | null } = {
+ status: -1,
+ data: null,
+ };
+ const isAbsUrl = absUrlPattern.test(path);
+ const uri = isAbsUrl ? path : isResource ? path.replace(/\/{2,}/i, '/') : (baseUri) + ('/' + path).replace(/\/{2,}/i, '/');
+ const init = {
+ 'method': 'GET',
+ ...initParam,
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Accept': 'application/json',
+ ...initParam.headers,
+ } as HeadersInit,
+ };
+ if (!isAbsUrl && authenticate && storeService.applicationStore) {
+ const { state: { framework: { authenticationState: { user } } } } = storeService.applicationStore;
+ // do not request if the user is not valid
+
+ if (!user || !user.isValid || !user.token || !user.tokenType) {
+ return {
+ ...result,
+ message: 'User is not valid or not logged in.',
+ };
+ }
+ (init.headers = {
+ ...init.headers,
+ 'Authorization': `${user.tokenType} ${user.token}`,
+ //'Authorization': 'Basic YWRtaW46YWRtaW4='
+ });
+ }
+
+ const fetchResult = await fetch(uri, init);
+ if (fetchResult.status === 309) {
+ const redirectUrl = fetchResult.headers.get('Location');
+ if (! redirectUrl) {
+ throw new Error('Status code 309 requires header "Location"');
+ }
+ localStorage.removeItem('userToken');
+ window.location.href = redirectUrl;
+ return {
+ ...result,
+ status: fetchResult.status,
+ message: 'Redirecting to new URL.',
+ };
+ } else if (fetchResult.status === 403) {
+ if (storeService.applicationStore) {
+ storeService.applicationStore.dispatch(new AddErrorInfoAction({ title: 'Forbidden', message:'Status: [403], access denied.' }));
+ }
+ return {
+ ...result,
+ status: 403,
+ message: 'Forbidden.',
+ };
+ } else if (fetchResult.status === 401) {
+ if (storeService.applicationStore) {
+ storeService.applicationStore.dispatch(new ReplaceAction(`/login?returnTo=${storeService.applicationStore.state.framework.navigationState.pathname}`));
+ }
+ return {
+ ...result,
+ status: 401,
+ message: 'Authentication requested by server.',
+ };
+ }
+ const contentType = fetchResult.headers.get('Content-Type') || fetchResult.headers.get('content-type');
+ const isJson = contentType && (contentType.toLowerCase().startsWith('application/json') || contentType.toLowerCase().startsWith('application/yang-data+json'));
+ try {
+ const data = (isJson ? await fetchResult.json() : await fetchResult.text()) as TData;
+ return {
+ ...result,
+ status: fetchResult.status,
+ message: fetchResult.statusText,
+ data: data,
+ };
+ } catch (error) {
+ return {
+ ...result,
+ status: fetchResult.status,
+ message: error && error.message || String(error),
+ data: undefined,
+ };
+ }
+}
+
+/** Sends a rest request to the given path.
+ * @returns The data, or null it there was any error
+ */
+export async function requestRest<TData>(path: string = '', init: RequestInit = {}, authenticate: boolean = true, isResource: boolean = false): Promise<TData | null | undefined> {
+ const res = await requestRestExt<TData>(path, init, authenticate, isResource);
+ if (res && res.status >= 200 && res.status < 300) {
+ return res.data;
+ }
+ return null;
+} \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/services/snackbarService.ts b/sdnr/wt-odlux/odlux/framework/src/services/snackbarService.ts
new file mode 100644
index 000000000..50e279c67
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/services/snackbarService.ts
@@ -0,0 +1,22 @@
+/**
+ * ============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 { OptionsObject } from "notistack";
+
+export const snackbarService = {
+ enqueueSnackbar: (message: string, options?: OptionsObject) =>{ }
+} \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/services/storeService.ts b/sdnr/wt-odlux/odlux/framework/src/services/storeService.ts
new file mode 100644
index 000000000..cbb5987de
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/services/storeService.ts
@@ -0,0 +1,11 @@
+import { ApplicationStore } from "../store/applicationStore";
+
+let applicationStore: ApplicationStore | null = null;
+
+export const startSoreService = (store: ApplicationStore) => {
+ applicationStore = store;
+};
+
+export const storeService = {
+ get applicationStore() { return applicationStore; },
+ }; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/services/userSessionService.ts b/sdnr/wt-odlux/odlux/framework/src/services/userSessionService.ts
new file mode 100644
index 000000000..8d899c4d6
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/services/userSessionService.ts
@@ -0,0 +1,80 @@
+/**
+ * ============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 { ApplicationStore } from "../store/applicationStore";
+import { logoutUser } from "../actions/authentication";
+import { ReplaceAction } from "../actions/navigationActions";
+import { AuthMessage, getBroadcastChannel } from "./broadcastService";
+import { User } from "../models/authentication";
+
+let currentUser: User | null;
+let applicationStore: ApplicationStore | null = null;
+let timer : null | ReturnType<typeof setTimeout> = null;
+
+export const startUserSessionService = (store: ApplicationStore) =>{
+ applicationStore=store;
+}
+
+export const startUserSession = (user: User) => {
+ console.log("user session started...")
+
+ const currentTime = new Date();
+ //get time differnce between login time and now (eg after user refreshes page)
+ const timeDiffernce =(currentTime.valueOf()/1000 - user.loginAt);
+
+ currentUser = user;
+
+ if (process.env.NODE_ENV === "development") {
+ //console.warn("logout timer not started in development mode");
+
+ const expiresIn = (user.logoutAt - user.loginAt) - timeDiffernce;
+ console.log("user should be logged out in: "+expiresIn/60 +"minutes")
+ createForceLogoutInterval(expiresIn);
+ } else {
+ const expiresIn = (user.logoutAt - user.loginAt) - timeDiffernce;
+ console.log("user should be logged out in: "+expiresIn/60 +"minutes")
+ createForceLogoutInterval(expiresIn);
+ }
+};
+
+const createForceLogoutInterval = (intervalInSec: number) => {
+ console.log("logout timer running...");
+
+ if(timer!==null){
+ console.error("an old session was available");
+ clearTimeout(timer);
+ }
+
+ timer = setTimeout(function () {
+ if (currentUser && applicationStore) {
+
+ applicationStore.dispatch(logoutUser());
+ applicationStore.dispatch(new ReplaceAction("/login"));
+
+ }
+
+ }, intervalInSec * 1000)
+}
+
+export const endUserSession = ()=>{
+
+ if(timer!==null){
+ clearTimeout(timer);
+ timer=null;
+ }
+} \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/services/userdataService.ts b/sdnr/wt-odlux/odlux/framework/src/services/userdataService.ts
new file mode 100644
index 000000000..53de8e1c3
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/services/userdataService.ts
@@ -0,0 +1,28 @@
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ */
+
+import { requestRest } from './restService';
+
+const settingsPath = '/userdata';
+
+export const getUserData = <TData>(partialPath?: string) => requestRest<TData>(partialPath ? settingsPath + partialPath : settingsPath, { method: 'GET' });
+
+export const saveUserData = <TData>(partialPath: string, data: string) => requestRest<TData>(settingsPath + partialPath, { method: 'PUT', body: data });
+
+
+
diff --git a/sdnr/wt-odlux/odlux/framework/src/store/applicationStore.ts b/sdnr/wt-odlux/odlux/framework/src/store/applicationStore.ts
new file mode 100644
index 000000000..cbe8c20da
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/store/applicationStore.ts
@@ -0,0 +1,74 @@
+/**
+ * ============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 { Store } from '../flux/store';
+import { combineActionHandler, MiddlewareArg, Middleware, chainMiddleware } from '../flux/middleware';
+
+import applicationService from '../services/applicationManager';
+
+import { applicationRegistryHandler, IApplicationRegistration } from '../handlers/applicationRegistryHandler';
+import { authenticationStateHandler, IAuthenticationState } from '../handlers/authenticationHandler';
+import { applicationStateHandler, IApplicationState } from '../handlers/applicationStateHandler';
+import { navigationStateHandler, INavigationState } from '../handlers/navigationStateHandler';
+
+import { setApplicationStore } from '../services/applicationApi';
+
+import apiMiddleware from '../middleware/api';
+import thunkMiddleware from '../middleware/thunk';
+import loggerMiddleware from '../middleware/logger';
+import routerMiddleware from '../middleware/navigation';
+import { updatePolicies } from '../middleware/policies';
+
+export type MiddlewareApi = MiddlewareArg<IApplicationStoreState>;
+
+export interface IFrameworkStoreState {
+ applicationRegistration: IApplicationRegistration;
+ applicationState: IApplicationState;
+ authenticationState: IAuthenticationState;
+ navigationState: INavigationState;
+}
+
+export interface IApplicationStoreState {
+ framework: IFrameworkStoreState;
+}
+
+const frameworkHandlers = combineActionHandler({
+ applicationRegistration: applicationRegistryHandler,
+ applicationState: applicationStateHandler,
+ authenticationState: authenticationStateHandler,
+ navigationState: navigationStateHandler
+});
+
+export class ApplicationStore extends Store<IApplicationStoreState> { }
+
+/** This function will create the application store considering the currently registered application ans their middlewares. */
+export const applicationStoreCreator = (): ApplicationStore => {
+ const middlewares: Middleware<IApplicationStoreState>[] = [];
+ const actionHandlers = Object.keys(applicationService.applications).reduce((acc, cur) => {
+ const reg = applicationService.applications[cur];
+ reg && typeof reg.rootActionHandler === 'function' && (acc[cur] = reg.rootActionHandler);
+ reg && reg.middlewares && Array.isArray(reg.middlewares) && middlewares.push(...(reg.middlewares as Middleware<IApplicationStoreState>[]));
+ return acc;
+ }, { framework: frameworkHandlers } as any);
+
+ const applicationStore = new ApplicationStore(combineActionHandler(actionHandlers), chainMiddleware(loggerMiddleware, thunkMiddleware, routerMiddleware, apiMiddleware, updatePolicies, ...middlewares));
+ setApplicationStore(applicationStore);
+ return applicationStore;
+}
+
+export default applicationStoreCreator; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/styles/att.ts b/sdnr/wt-odlux/odlux/framework/src/styles/att.ts
new file mode 100644
index 000000000..2d54590c9
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/styles/att.ts
@@ -0,0 +1,46 @@
+/**
+ * ============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 { createTheme, adaptV4Theme } from '@mui/material/styles';
+
+const theme = createTheme(adaptV4Theme({
+ design: {
+ id: "att",
+ name: "AT&T",
+ url: "https://pmcvariety.files.wordpress.com/2016/04/att_logo.jpg?w=1000&h=563&crop=1",
+ height: 70,
+ width: 150,
+ logoHeight: 60,
+ },
+ palette: {
+ primary: {
+ light: "#f2f2f29c",
+ main: "#f2f2f2",
+ dark: "#d5d5d5",
+ contrastText: "#0094d3"
+ },
+ secondary: {
+ light: "#f2f2f2",
+ main: "rgba(51, 171, 226, 1)",
+ dark: "rgba(41, 159, 213, 1)",
+ contrastText: "#0094d3"
+ }
+ },
+}));
+
+export default theme;
diff --git a/sdnr/wt-odlux/odlux/framework/src/utilities/elasticSearch.ts b/sdnr/wt-odlux/odlux/framework/src/utilities/elasticSearch.ts
new file mode 100644
index 000000000..e1d37522d
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/utilities/elasticSearch.ts
@@ -0,0 +1,114 @@
+/**
+ * ============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 { Result, ResultTopology } from '../models';
+import { DataCallback } from '../components/material-table';
+
+import { requestRest } from '../services/restService';
+
+import { convertPropertyNames, convertPropertyValues, replaceUpperCase, replaceHyphen } from './yangHelper';
+
+type propType = string | number | null | undefined | (string | number)[];
+type dataType = { [prop: string]: propType };
+
+/** Represents a fabric for the searchDataHandler used by the internal data api.
+ * @param typeName The name of the entry type to create a searchDataHandler for.
+ * @param additionalFilters Filterproperties and their values to add permanently.
+ * @returns The searchDataHandler callback to be used with the material table.
+*/
+export function createSearchDataHandler<TResult>(typeName: (() => string) | string, connectToTopologyServer?: boolean, additionalFilters?: {} | null | undefined): DataCallback<(TResult)> {
+ const fetchData: DataCallback<(TResult)> = async (pageIndex, rowsPerPage, orderBy, order, filter) => {
+
+ const topologyUrl = `/topology/network/read-${typeof typeName === "function" ? typeName() : typeName}-list`;
+ const dataProviderUrl = `/rests/operations/data-provider:read-${typeof typeName === "function" ? typeName() : typeName}-list`;
+
+ const url = connectToTopologyServer ? topologyUrl : dataProviderUrl;
+
+ filter = { ...filter, ...additionalFilters };
+
+ const filterKeys = filter && Object.keys(filter) || [];
+
+ const input = {
+ filter: filterKeys.filter(f => filter![f] != null && filter![f] !== "").map(property => ({ property, filtervalue: filter![property] })),
+ sortorder: orderBy ? [{ property: orderBy, sortorder: order === "desc" ? "descending" : "ascending" }] : [],
+ pagination: { size: rowsPerPage, page: (pageIndex != null && pageIndex > 0 && pageIndex || 0) + 1 }
+ }
+
+ if (url.includes('data-provider')) {
+ const query = {
+ "data-provider:input": input
+ };
+
+ const result = await requestRest<Result<TResult>>(url, {
+ method: "POST", // *GET, POST, PUT, DELETE, etc.
+ mode: "same-origin", // no-cors, cors, *same-origin
+ cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
+ headers: {
+ "Content-Type": "application/json",
+ // "Content-Type": "application/x-www-form-urlencoded",
+ },
+ body: JSON.stringify(convertPropertyValues(query, replaceUpperCase)), // body data type must match "Content-Type" header
+ });
+ if (result) {
+ let rows: TResult[] = [];
+
+ if (result && result["data-provider:output"] && result["data-provider:output"].data) {
+ rows = result["data-provider:output"].data.map(obj => convertPropertyNames(obj, replaceHyphen)) || []
+ }
+
+ const data = {
+ page: +(result["data-provider:output"].pagination && result["data-provider:output"].pagination.page != null && result["data-provider:output"].pagination.page - 1 || 0), total: +(result["data-provider:output"].pagination && result["data-provider:output"].pagination.total || 0), rows: rows
+ };
+ return data;
+ }
+ } else if (url.includes('topology')) {
+
+ const queryTopology = {
+ "input": input
+ };
+
+ const resultTopology = await requestRest<ResultTopology<TResult>>(url, {
+ method: "POST", // *GET, POST, PUT, DELETE, etc.
+ mode: "same-origin", // no-cors, cors, *same-origin
+ cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
+ headers: {
+ "Content-Type": "application/json",
+ // "Content-Type": "application/x-www-form-urlencoded",
+ },
+ body: JSON.stringify(queryTopology), // body data type must match "Content-Type" header
+ });
+ if (resultTopology) {
+ let rows: TResult[] = [];
+
+ if (resultTopology && resultTopology.output && resultTopology.output.data) {
+ rows = resultTopology.output.data.map(obj => obj) || []
+ }
+
+ const data = {
+ page: +(resultTopology.output.pagination && resultTopology.output.pagination.page != null && resultTopology.output.pagination.page - 1 || 0), total: +(resultTopology.output.pagination && resultTopology.output.pagination.total || 0), rows: rows
+ };
+ return data;
+ }
+ }
+
+ return { page: 1, total: 0, rows: [] };
+ };
+
+ return fetchData;
+}
+
diff --git a/sdnr/wt-odlux/odlux/framework/src/utilities/logLevel.ts b/sdnr/wt-odlux/odlux/framework/src/utilities/logLevel.ts
new file mode 100644
index 000000000..a198d98a9
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/utilities/logLevel.ts
@@ -0,0 +1,8 @@
+export enum LogLevel {
+ Always = 0,
+ Error = 1,
+ Warning = 2,
+ Info = 3,
+ Debug = 4,
+ Trace = 5,
+}
diff --git a/sdnr/wt-odlux/odlux/framework/src/utilities/withComponents.ts b/sdnr/wt-odlux/odlux/framework/src/utilities/withComponents.ts
new file mode 100644
index 000000000..8e460ad4c
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/utilities/withComponents.ts
@@ -0,0 +1,37 @@
+/**
+ * ============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 applicationService from '../services/applicationManager';
+export type WithComponents<T extends { [name: string]: string }> = {
+ components: { [prop in keyof T]: React.ComponentType }
+};
+
+export function withComponents<TProps,TMap extends { [name: string]: string }>(mapping: TMap) {
+ return (component: React.ComponentType<TProps & WithComponents<TMap>>): React.ComponentType<TProps> => {
+ const components = {} as any;
+ Object.keys(mapping).forEach(name => {
+ const [appKey, componentKey] = mapping[name].split('.');
+ const reg = applicationService.applications[appKey];
+ components[name] = reg && reg.exportedComponents && reg.exportedComponents[componentKey] || (() => null);
+ });
+ return (props: TProps) => (
+ React.createElement(component, Object.assign({ components }, props))
+ );
+ }
+}
+export default withComponents; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/utilities/yangHelper.ts b/sdnr/wt-odlux/odlux/framework/src/utilities/yangHelper.ts
new file mode 100644
index 000000000..7e77c055c
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/utilities/yangHelper.ts
@@ -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==========================================================================
+ */
+
+
+export const replaceHyphen = (name: string) => name.replace(/-([a-z])/g, (g) => (g[1].toUpperCase()));
+export const replaceUpperCase = (name: string) => name.replace(/([a-z][A-Z])/g, (g) => g[0] + '-' + g[1].toLowerCase());
+
+/***
+ * Replaces whitespace with '-' and cast everything to lowercase
+ */
+export const toAriaLabel = (value: string) => value.replace(/\s/g, "-").toLowerCase();
+
+export const convertPropertyNames = <T extends { [prop: string]: any }>(obj: T, conv: (name: string) => string): T => {
+ return Object.keys(obj).reduce<{ [prop: string]: any }>((acc, cur) => {
+ acc[conv(cur)] = typeof obj[cur] === "object" ? convertPropertyNames(obj[cur], conv) : obj[cur];
+ return acc;
+ }, obj instanceof Array ? [] : {}) as T;
+}
+
+export const convertPropertyValues = <T extends { [prop: string]: any }>(obj: T, conv: (name: string) => string): T => {
+ return Object.keys(obj).reduce<{ [prop: string]: any }>((acc, cur) => {
+ acc[cur] = typeof obj[cur] === "object"
+ ? convertPropertyValues(obj[cur], conv)
+ : cur === "property"
+ ? conv(obj[cur])
+ : obj[cur];
+ return acc;
+ }, obj instanceof Array ? [] : {}) as T;
+} \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/views/about.tsx b/sdnr/wt-odlux/odlux/framework/src/views/about.tsx
new file mode 100644
index 000000000..72358bf84
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/views/about.tsx
@@ -0,0 +1,196 @@
+/**
+ * ============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, { FC, useEffect, useState } from 'react';
+import * as marked from 'marked';
+import * as hljs from 'highlight.js';
+import { requestRestExt } from '../services/restService';
+import { Button, Typography } from '@mui/material';
+
+const defaultRenderer = new marked.Renderer();
+defaultRenderer.link = (href, title, text) => (
+ `<a target="_blank" rel="noopener noreferrer" href="${href}" title="${title}">${text}</a>`
+);
+
+type OdluxVersion= {version:string,build:string, framework: string,
+ applications:{
+ configurationApp: string,
+ connectApp: string,
+ eventLogApp: string,
+ faultApp: string,
+ helpApp: string,
+ inventoryApp: string,
+ microwaveApp: string,
+ maintenanceApp: string,
+ mediatorApp: string,
+ networkMapApp: string,
+ permanceHistoryApp: string,
+ siteManagerApp: string,
+ }};
+
+type TopologyVersion = {version: string, buildTimestamp: string};
+
+const AboutComponent: FC = (props) => {
+
+ const textareaRef = React.createRef<HTMLTextAreaElement>();
+ const [content, setContent] = useState<string | null>(null);
+ const [isCopiedSuccessfully, setCopySuccess] = useState(false);
+ const [isContetLoaded, setContentLoaded] = useState(false);
+
+ useEffect(()=>{
+ loadAboutContent();
+ },[]);
+
+ const getMarkOdluxVersionMarkdownTable = (data:OdluxVersion|null|undefined):string => {
+ if(!data) {
+ return "";
+ }else{
+ let applicationVersions= '';
+ if(data.applications){
+
+ applicationVersions = `| Framework | ${data.framework}|\n `+
+ `| ConnectApp | ${data.applications.connectApp}|\n `+
+ `| FaultApp | ${data.applications.faultApp}|\n `+
+ `| MaintenanceApp | ${data.applications.maintenanceApp}|\n `+
+ `| ConfigurationApp | ${data.applications.configurationApp}|\n `+
+ `| PerformanceHistoryApp | ${data.applications.permanceHistoryApp}|\n `+
+ `| InventoryApp | ${data.applications.inventoryApp}|\n `+
+ `| EventLogApp | ${data.applications.eventLogApp}|\n `+
+ `| MediatorApp | ${data.applications.mediatorApp}|\n `+
+ `| NetworkMapApp | ${data.applications.networkMapApp}|\n `+
+ `| MicrowaveApp | ${data.applications.microwaveApp}|\n `+
+ `| SiteManagerApp | ${data.applications.siteManagerApp}|\n `+
+ `| HelpApp | ${data.applications.helpApp}|\n `;
+ }
+
+ return `| | |\n| --- | --- |\n| Version | ${data.version} |\n| Build timestamp | ${data.build}|\n`+
+ applicationVersions;
+ }
+ }
+
+ const getTopologyVersionMarkdownTable = (data: TopologyVersion|null|undefined) => {
+ if(!data){
+ return "No version";
+ }
+ else
+ {
+ const topologyInfo = `| | |\n| --- | --- |\n| Version | ${data.version} |\n` +
+ `| Build timestamp | ${data.buildTimestamp} |\n`;
+ return topologyInfo;
+ }
+ }
+
+ const loadAboutContent = (): void => {
+ const baseUri = window.location.pathname.substring(0,window.location.pathname.lastIndexOf("/")+1);
+ const init = {
+ 'method': 'GET',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Accept': 'text/markdown',
+ }
+ };
+ const p1 = requestRestExt<string>('/about',init);
+ const p2 = requestRestExt<OdluxVersion>(`${baseUri}version.json`);
+ const p3 = requestRestExt<any>(`/topology/info/version`);
+
+ Promise.all([p1,p2, p3]).then((responses) => {
+ const response = responses[0];
+ const response2 = responses[1];
+ const response3 = responses[2];
+ const content = response.status == 200 ? response.data : `${response.status} ${response.message}` || "Server error";
+ const content2 = `\n## ODLUX Version Info\n`+(response2.status == 200 ? getMarkOdluxVersionMarkdownTable(response2.data) : `${response2.message}` || "ODLUX Server error");
+ const content3 = `\n## Topology API Version Info\n`+(response3.status == 200 ? getTopologyVersionMarkdownTable(response3.data): `Topology API not available`);
+ const loadedSucessfully = response.status == 200 ? true : false;
+ setContent((content + content2 + content3 ) || null);
+ setContentLoaded(loadedSucessfully);
+ }).catch((error) => {
+ setContent(error);
+ });
+ }
+
+ const copyToClipboard = (e: React.MouseEvent<HTMLButtonElement>) =>{
+ e.preventDefault();
+
+ if(textareaRef.current!==null){
+ textareaRef.current.select();
+ document.execCommand('copy');
+ if(e.currentTarget != null){ // refocus on button, otherwhise the textarea would be focused
+ e.currentTarget.focus();
+ }
+ setCopySuccess(true);
+ window.setTimeout(()=>{ setCopySuccess(false);},2000);
+ }
+ }
+
+ const markedOptions: marked.MarkedOptions = {
+ gfm: true,
+ breaks: false,
+ pedantic: false,
+ sanitize: true,
+ smartLists: true,
+ smartypants: false,
+ langPrefix: 'hljs ',
+ ...({}),
+ highlight: (code, lang) => {
+ if (!!(lang && hljs.getLanguage(lang))) {
+ return hljs.highlight(lang, code).value;
+ }
+ return code;
+ }
+ };
+
+
+ const className = "about-table"
+ const style: React.CSSProperties = {};
+ const containerStyle = { overflow: "auto", paddingRight: "20px" }
+
+ const html = (marked(content || 'loading', { renderer: markedOptions && markedOptions.renderer || defaultRenderer }));
+
+ return (
+ <div style={containerStyle}>
+ { isContetLoaded &&
+ <div style={{float: "right", marginRight: "10px"}}>
+ <Button aria-label="copy-version-information-button" color="inherit" variant="contained" onClick={e => copyToClipboard(e)}>
+ Copy to clipboard
+ </Button>
+ {
+ isCopiedSuccessfully &&
+ <Typography variant="body1" style={{color: "green"}} align="center">
+ copied successfully
+ </Typography>
+ }
+ </div>
+ }
+
+ <div
+ dangerouslySetInnerHTML={{ __html: html }}
+ className={className}
+ style={style}
+ />
+ <form>
+ <textarea
+ style={{opacity: ".01"}}
+ ref={textareaRef}
+ value={content || ''}
+ />
+ </form>
+ </div>
+ );
+};
+
+export const About = AboutComponent;
+export default About; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/views/frame.tsx b/sdnr/wt-odlux/odlux/framework/src/views/frame.tsx
new file mode 100644
index 000000000..2c9f8c033
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/views/frame.tsx
@@ -0,0 +1,134 @@
+/**
+ * ============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, { FC, memo } from 'react';
+import { Router, Route, Redirect, Switch } from 'react-router-dom';
+
+import { Theme } from '@mui/material/styles';
+import { makeStyles } from '@mui/styles';
+
+import { SnackbarProvider } from 'notistack';
+import { ConfirmProvider } from 'material-ui-confirm';
+
+import AppFrame from '../components/routing/appFrame';
+import TitleBar from '../components/titleBar';
+import Menu from '../components/navigationMenu';
+import ErrorDisplay from '../components/errorDisplay';
+import SnackDisplay from '../components/material-ui/snackDisplay';
+
+import Home from '../views/home';
+import Login from '../views/login';
+import About from '../views/about';
+import Test from '../views/test';
+import UserSettings from '../views/settings';
+
+import applicationService from '../services/applicationManager';
+
+import { history } from '../middleware/navigation';
+
+const aboutIcon = require('../assets/icons/About.svg');
+const homeIcon = require('../assets/icons/Home.svg');
+const loginIcon = require('../assets/icons/User.svg');
+const settingsIcon = require('../assets/icons/Tools.svg');
+
+
+const styles = makeStyles((theme: Theme) => {
+
+ return {
+ root: {
+ flexGrow: 1,
+ height: '100%',
+ zIndex: 1,
+ overflow: 'hidden',
+ position: 'relative',
+ display: 'flex',
+ },
+ content: {
+ flexGrow: 1,
+ display: "flex",
+ flexDirection: "column",
+ backgroundColor: '#fafafa',
+ padding: theme.spacing(3),
+ minWidth: 0, // So the Typography noWrap works
+ },
+ toolbar: theme.mixins.toolbar as any
+ };
+});
+
+const FrameComponent: FC = memo(() => {
+
+ const registrations = applicationService.applications;
+ const classes = styles();
+ return (
+ <ConfirmProvider>
+ <SnackbarProvider maxSnack={3}>
+ <Router history={history as any} >
+ <div className={classes.root}>
+ <SnackDisplay />
+ <ErrorDisplay />
+ <TitleBar />
+ <Menu />
+ <main className={classes.content}>
+ {
+ <div className={classes.toolbar} /> //needed for margins, don't remove!
+ }
+ <Switch>
+ <Route exact path="/" component={() => (
+ <AppFrame title={"Home"} icon={homeIcon} >
+ <Home />
+ </AppFrame>
+ )} />
+ <Route path="/about" component={() => (
+ <AppFrame title={"About"} icon={aboutIcon} >
+ <About />
+ </AppFrame>
+ )} />
+ <Route path="/settings" component={() => (
+ <AppFrame title={"Settings"} icon={settingsIcon} >
+ <UserSettings />
+ </AppFrame>
+ )} />
+ {process.env.NODE_ENV === "development" ? <Route path="/test" component={() => (
+ <AppFrame title={"Test"} icon={settingsIcon} >
+ <Test />
+ </AppFrame>
+ )} /> : null}
+ <Route path="/login" component={() => (
+ <AppFrame title={"Login"} icon={loginIcon} >
+ <Login />
+ </AppFrame>
+ )} />
+ {Object.keys(registrations).map(p => {
+ const application = registrations[p];
+ return (<Route key={application.name} path={application.path || `/${application.name}`} component={() => (
+ <AppFrame title={application.title || (typeof application.menuEntry === 'string' && application.menuEntry) || application.name} icon={application.icon} appId={application.name} >
+ <application.rootComponent />
+ </AppFrame>
+ )} />)
+ })}
+ <Redirect to="/" />
+ </Switch>
+ </main>
+ </div>
+ </Router>
+ </SnackbarProvider>
+ </ConfirmProvider>
+ );
+});
+
+export const Frame = FrameComponent;
+export default Frame;
diff --git a/sdnr/wt-odlux/odlux/framework/src/views/home.tsx b/sdnr/wt-odlux/odlux/framework/src/views/home.tsx
new file mode 100644
index 000000000..72c5059e1
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/views/home.tsx
@@ -0,0 +1,59 @@
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ */
+
+import React, {FC, useState} from 'react';
+import applicationService from '../services/applicationManager';
+
+
+type DashboardElement = { name: string, element: JSX.Element };
+
+const DashboardView: FC = (props) => {
+
+ const registrations = applicationService.applications;
+
+ const [selectedIndex] = useState(0);
+
+ let dashboardArray: DashboardElement[] = [];
+
+ let dashboardElements: (DashboardElement)[] = Object.keys(registrations).map(p => {
+ const application = registrations[p];
+
+ if (application.dashbaordElement) {
+ const value: DashboardElement = { name: application.menuEntry?.toString()!, element: <application.dashbaordElement /> };
+ return value;
+
+ } else {
+ return null;
+ }
+ }).filter((x): x is DashboardElement => x !== null);
+
+ dashboardArray.push(...dashboardElements);
+
+ return <div>
+ <div>
+ <div>
+ {
+ dashboardArray[selectedIndex]?.element
+ }
+ </div>
+ </div>
+ </div>
+}
+
+
+export default DashboardView; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/views/login.tsx b/sdnr/wt-odlux/odlux/framework/src/views/login.tsx
new file mode 100644
index 000000000..46c0872bc
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/views/login.tsx
@@ -0,0 +1,250 @@
+/**
+ * ============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, { FC, useEffect, useState } from 'react';
+import { RouteComponentProps, withRouter } from 'react-router-dom';
+
+import Alert from '@mui/material/Alert';
+import Avatar from '@mui/material/Avatar';
+import Button from '@mui/material/Button';
+import CssBaseline from '@mui/material/CssBaseline';
+import FormControl from '@mui/material/FormControl';
+import Input from '@mui/material/Input';
+import InputLabel from '@mui/material/InputLabel';
+import Paper from '@mui/material/Paper';
+import { Theme } from '@mui/material/styles';
+import Typography from '@mui/material/Typography';
+
+import { makeStyles } from '@mui/styles';
+
+import { useApplicationDispatch, useSelectApplicationState } from '../flux/connect';
+import authenticationService from '../services/authenticationService';
+
+import { loginUserAction, UpdatePolicies } from '../actions/authentication';
+import { updateExternalLoginProviderAsyncActionCreator } from '../actions/loginProvider';
+
+import { AuthPolicy, AuthToken, User } from '../models/authentication';
+
+const loginIcon = require('../assets/icons/User.svg');
+
+const styles = makeStyles((theme: Theme) =>{
+ return{
+ layout: {
+ width: 'auto',
+ display: 'block', // Fix IE11 issue.
+ marginLeft: theme.spacing(3),
+ marginRight: theme.spacing(3),
+ [theme.breakpoints.up(400 + Number(theme.spacing(3).replace('px','')) * 2)]: {
+ width: 400,
+ marginLeft: 'auto',
+ marginRight: 'auto',
+ },
+ },
+ paper: {
+ marginTop: theme.spacing(8),
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ padding: `${theme.spacing(2)} ${theme.spacing(3)} ${theme.spacing(3)}`,
+ },
+ avatar: {
+ margin: theme.spacing(1),
+ backgroundColor: theme.palette.secondary.main,
+ },
+ form: {
+ width: '100%', // Fix IE11 issue.
+ marginTop: theme.spacing(1),
+ },
+ submit: {
+ marginTop: theme.spacing(3),
+ },
+ lineContainer:{
+ width: '100%',
+ height: 10,
+ borderBottom: '1px solid grey',
+ textAlign: 'center',
+ marginTop:15,
+ marginBottom:5
+ },
+ thirdPartyDivider:{
+ fontSize: 15,
+ backgroundColor: 'white',
+ padding: '0 10px',
+ color: 'grey'
+ }
+};
+});
+
+
+type LoginProps = RouteComponentProps;
+
+// todo: ggf. redirect to einbauen
+const LoginComponent: FC<LoginProps> = (props) => {
+
+ const search = useSelectApplicationState(state => state.framework.navigationState.search);
+ const authentication = useSelectApplicationState(state => state.framework.applicationState.authentication);
+ const externalLoginProviders = useSelectApplicationState(state => state.framework.applicationState.externalLoginProviders);
+
+ const dispatch = useApplicationDispatch();
+ const updateExternalProviders = () => dispatch(updateExternalLoginProviderAsyncActionCreator());
+ const updateAuthentication = (token: AuthToken | null) => {
+ const user = token && new User(token) || undefined;
+ dispatch(loginUserAction(user));
+ }
+ const updatePolicies = (policies?: AuthPolicy[]) => {
+ return dispatch(new UpdatePolicies(policies));
+ }
+
+ const [isBusy, setBusy] = useState(false);
+ const [username, setUsername] = useState("");
+ const [password, setPassword] = useState("");
+ const [scope, setScope] = useState("sdn");
+ const [message, setMessage] = useState("");
+ const [isServerReady, setIsServerReady] = useState(false);
+
+ useEffect(()=>{
+ if (authentication === "oauth" && (externalLoginProviders == null || externalLoginProviders.length === 0)){
+ updateExternalProviders();
+ }
+
+ authenticationService.getServerReadyState().then(result =>{
+ setIsServerReady(result);
+ })
+ },[]);
+
+ const onSignIn = async (event: React.MouseEvent<HTMLButtonElement>) => {
+ event.preventDefault();
+
+ setBusy(true);
+
+ const token = authentication === "oauth"
+ ? await authenticationService.authenticateUserOAuth(username, password, scope)
+ : await authenticationService.authenticateUserBasicAuth(username, password, scope);
+
+ updateAuthentication(token);
+ setBusy(false);
+
+ if (token) {
+ const query = search && search.replace(/^\?/, "").split('&').map(e => e.split("="));
+ const returnTo = query && query.find(e => e[0] === "returnTo");
+ props.history.replace(returnTo && returnTo[1] || "/");
+ }
+ else {
+
+ if(!isServerReady){
+ const ready = await authenticationService.getServerReadyState();
+ if(ready){
+ setIsServerReady(true);
+ }else{
+ setMessage("Login is currently not possible. Please re-try in a few minutes. If the problem persists, ask your administrator for assistance.");
+ }
+
+ }else{
+ setMessage("Could not log in. Please check your credentials or ask your administrator for assistance.");
+ setPassword("");
+ }
+ }
+ }
+
+ const classes = styles();
+ const areProvidersAvailable = externalLoginProviders && externalLoginProviders.length > 0;
+
+ return (
+ <>
+ <CssBaseline />
+ <main className={classes.layout}>
+ <Paper className={classes.paper}>
+ <Avatar className={classes.avatar}>
+ <img src={loginIcon} alt="loginIcon" />
+ </Avatar>
+ <Typography variant="caption">Sign in</Typography>
+ <form className={classes.form}>
+ {areProvidersAvailable &&
+ <>
+ {
+ externalLoginProviders!.map((provider, index) => (
+ <Button
+ aria-controls="externalLogin"
+ aria-label={"external-login-identity-provider-" + (index + 1)}
+ aria-haspopup="true"
+ fullWidth
+ variant="contained"
+ color="inherit"
+ className={classes.submit} onClick={() => { window.location = provider.loginUrl as any; }}>
+ {provider.title}
+ </Button>))
+ }
+ <div className={classes.lineContainer}>
+ <span className={classes.thirdPartyDivider}>
+ OR
+ </span>
+ </div>
+ </>
+ }
+ <FormControl variant="standard" margin="normal" required fullWidth>
+ <InputLabel htmlFor="username">Username</InputLabel>
+ <Input id="username" name="username" autoComplete="username" autoFocus
+ disabled={isBusy}
+ value={username}
+ onChange={event => { setUsername(event.target.value); }} />
+ </FormControl>
+ <FormControl variant="standard" margin="normal" required fullWidth>
+ <InputLabel htmlFor="password">Password</InputLabel>
+ <Input
+ name="password"
+ type="password"
+ id="password"
+ autoComplete="current-password"
+ disabled={isBusy}
+ value={password}
+ onChange={event => { setPassword(event.target.value); }}
+ />
+ </FormControl>
+ <FormControl variant="standard" margin="normal" required fullWidth>
+ <InputLabel htmlFor="password">Domain</InputLabel>
+ <Input
+ name="scope"
+ type="scope"
+ id="scope"
+ disabled={isBusy}
+ value={scope}
+ onChange={event => { setScope(event.target.value); }}
+ />
+ </FormControl>
+ <Button
+ aria-label="login-button"
+ type="submit"
+ fullWidth
+ variant="contained"
+ color="inherit"
+ disabled={isBusy}
+ className={classes.submit}
+ onClick={onSignIn}
+ >
+ Sign in
+ </Button>
+
+ </form>
+ {message && <Alert severity="error">{message}</Alert>}
+ </Paper>
+ </main>
+ </>
+ );
+}
+
+export const Login = withRouter(LoginComponent);
+export default Login; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/views/settings.tsx b/sdnr/wt-odlux/odlux/framework/src/views/settings.tsx
new file mode 100644
index 000000000..5973db9a0
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/views/settings.tsx
@@ -0,0 +1,115 @@
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ */
+
+import React, {FC, useState } from 'react';
+import { useApplicationDispatch } from "../flux/connect";
+
+import { Divider, List, ListItem, ListItemText, Paper } from '@mui/material';
+import { makeStyles } from '@mui/styles';
+import applicationService from '../services/applicationManager';
+
+import { GoBackAction } from '../actions/navigationActions';
+import { GeneralUserSettings } from '../components/settings/general';
+import { toAriaLabel } from '../utilities/yangHelper';
+
+type SettingsEntry = { name: string, element: JSX.Element };
+
+const styles = makeStyles({
+ sectionMargin: {
+ marginTop: "30px",
+ marginBottom: "15px"
+ },
+ elementMargin: {
+ marginLeft: "10px"
+ },
+ menu: {
+ flex: "1 0 0%",
+ }
+});
+
+const UserSettings: FC = (props) => {
+
+ const dispatch = useApplicationDispatch();
+ const goBack = () => dispatch(new GoBackAction());
+
+ const [selectedIndex, setSelectedIndex] = useState(0);
+
+ const registrations = applicationService.applications;
+
+ const navigateBack = () => {
+ goBack();
+ }
+
+ let settingsArray: SettingsEntry[] = [];
+
+ //add all framework specific settings
+ settingsArray.push({name:"General", element: <GeneralUserSettings onClose={navigateBack} />})
+
+ //get app settings
+ let settingsElements : (SettingsEntry) [] = Object.keys(registrations).map(p => {
+ const application = registrations[p];
+
+ if (application.settingsElement) {
+ const value: SettingsEntry = { name: application.menuEntry?.toString()!, element: <application.settingsElement onClose={navigateBack} /> };
+ return value;
+
+ } else {
+ return null;
+ }
+ }).filter((x): x is SettingsEntry => x !== null);
+
+
+ settingsArray.push(...settingsElements);
+
+ const onSelectElement = (e: any, newValue: number) => {
+ e.preventDefault();
+ setSelectedIndex(newValue);
+ }
+
+ const classes = styles();
+
+ return <div style={{ display: "flex", flexDirection: "row", height: "100%" }}>
+ <div style={{ display: "flex", flexDirection: "column", height: "100%", width: "15%" }}>
+ <Paper variant="outlined" style={{ height: "70%" }}>
+ <List className={classes.menu} component="nav">
+ {
+ settingsArray.map((el, index) => {
+ return (
+ <>
+ <ListItem key={"settings-key-"+index} selected={selectedIndex === index} button onClick={e => { onSelectElement(e, index) }} aria-label={toAriaLabel(el?.name+"-settings")}>
+ <ListItemText primary={el?.name} style={{ padding: 0 }} />
+ </ListItem>
+ <Divider />
+ </>)
+ })
+ }
+ </List>
+ </Paper>
+ </div>
+ <div style={{ height: "100%", width: "80%", marginLeft: 15 }}>
+ <div style={{ height: "100%" }}>
+ {
+ settingsArray[selectedIndex]?.element
+ }
+ </div>
+ </div>
+ </div>
+}
+
+
+export default UserSettings; \ No newline at end of file
diff --git a/sdnr/wt-odlux/odlux/framework/src/views/test.tsx b/sdnr/wt-odlux/odlux/framework/src/views/test.tsx
new file mode 100644
index 000000000..72f8d2cc8
--- /dev/null
+++ b/sdnr/wt-odlux/odlux/framework/src/views/test.tsx
@@ -0,0 +1,877 @@
+/**
+ * ============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 { withComponents, WithComponents } from '../utilities/withComponents';
+import { Theme } from '@mui/material/styles';
+
+import { WithStyles } from '@mui/styles';
+import withStyles from '@mui/styles/withStyles';
+import createStyles from '@mui/styles/createStyles';
+
+import Accordion from '@mui/material/Accordion';
+import AccordionSummary from '@mui/material/AccordionSummary';
+import AccordionDetails from '@mui/material/AccordionDetails';
+import Typography from '@mui/material/Typography';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
+
+import { MaterialTable, MaterialTableCtorType, ColumnType } from '../components/material-table';
+import { TreeView, TreeItem, TreeViewCtorType } from '../components/material-ui/treeView';
+import { SvgIconProps } from '@mui/material/SvgIcon';
+
+const styles = (theme: Theme) => createStyles({
+ root: {
+ width: '100%',
+ },
+ heading: {
+ fontSize: theme.typography.pxToRem(15),
+ fontWeight: theme.typography.fontWeightRegular,
+ },
+});
+
+class SampleData {
+ _id: string;
+ index: number;
+ guid: string;
+ isActive: boolean;
+ balance: string;
+ age: number;
+ firstName: string;
+ lastName: string;
+ company: string;
+ email: string;
+ registered: string;
+ latitude: string;
+ longitude: string;
+}
+
+// https://next.json-generator.com/NJ5Bv-v1I
+const tableData: SampleData[] = [
+ {
+ "_id": "5c0e18399919a5c43636fdf2",
+ "index": 0,
+ "guid": "48728d8e-8300-4d0f-b967-e2166d023066",
+ "isActive": false,
+ "balance": "$3,480.16",
+ "age": 33,
+ "firstName": "Brooke",
+ "lastName": "Morris",
+ "company": "ZORROMOP",
+ "email": "brooke.morris@zorromop.de",
+ "registered": "Sunday, February 11, 2018 2:55 PM",
+ "latitude": "-69.109379",
+ "longitude": "113.735639"
+ },
+ {
+ "_id": "5c0e1839b61e3eeaf164259d",
+ "index": 1,
+ "guid": "28723570-1507-422e-b78c-924402371fb1",
+ "isActive": false,
+ "balance": "$1,305.01",
+ "age": 28,
+ "firstName": "Jolene",
+ "lastName": "Everett",
+ "company": "ZENCO",
+ "email": "jolene.everett@zenco.de",
+ "registered": "Saturday, December 8, 2018 5:17 PM",
+ "latitude": "13.683025",
+ "longitude": "85.101421"
+ },
+ {
+ "_id": "5c0e1839e81f57913c5d2147",
+ "index": 2,
+ "guid": "e914dc5d-91a3-405d-ac48-aee6f0cd391a",
+ "isActive": true,
+ "balance": "$1,418.37",
+ "age": 28,
+ "firstName": "Elva",
+ "lastName": "Travis",
+ "company": "ZYTREK",
+ "email": "elva.travis@zytrek.de",
+ "registered": "Thursday, March 10, 2016 5:13 PM",
+ "latitude": "53.75862",
+ "longitude": "-67.784532"
+ },
+ {
+ "_id": "5c0e1839bc9224a2b54c0f69",
+ "index": 3,
+ "guid": "88cbdce0-0bcc-4d16-83c3-3017690503c4",
+ "isActive": true,
+ "balance": "$1,709.60",
+ "age": 21,
+ "firstName": "Ellis",
+ "lastName": "Mcpherson",
+ "company": "DIGIPRINT",
+ "email": "ellis.mcpherson@digiprint.de",
+ "registered": "Sunday, December 21, 2014 5:25 AM",
+ "latitude": "46.486149",
+ "longitude": "-66.657067"
+ },
+ {
+ "_id": "5c0e183951b51475db0f35d1",
+ "index": 4,
+ "guid": "c887ac86-7ba1-4eb6-9b47-e88a1bcb3713",
+ "isActive": true,
+ "balance": "$3,578.54",
+ "age": 25,
+ "firstName": "Marcia",
+ "lastName": "Rocha",
+ "company": "ZAPPIX",
+ "email": "marcia.rocha@zappix.de",
+ "registered": "Tuesday, June 16, 2015 11:21 AM",
+ "latitude": "-39.905461",
+ "longitude": "150.873895"
+ },
+ {
+ "_id": "5c0e18398c5be8d362a578eb",
+ "index": 5,
+ "guid": "0d160697-9b5b-4941-9b5f-4ba3a7f97b49",
+ "isActive": true,
+ "balance": "$414.98",
+ "age": 32,
+ "firstName": "Lavonne",
+ "lastName": "Wilkins",
+ "company": "FARMAGE",
+ "email": "lavonne.wilkins@farmage.de",
+ "registered": "Monday, February 1, 2016 5:27 PM",
+ "latitude": "-16.839256",
+ "longitude": "-105.824746"
+ },
+ {
+ "_id": "5c0e18399804086c836d7d56",
+ "index": 6,
+ "guid": "715a5f63-35b6-4903-a46e-ba584b005e64",
+ "isActive": false,
+ "balance": "$1,755.78",
+ "age": 32,
+ "firstName": "Wise",
+ "lastName": "Berg",
+ "company": "ZIZZLE",
+ "email": "wise.berg@zizzle.de",
+ "registered": "Saturday, March 28, 2015 1:40 AM",
+ "latitude": "51.15269",
+ "longitude": "65.795093"
+ },
+ {
+ "_id": "5c0e18399c4d13538bcaf8c9",
+ "index": 7,
+ "guid": "7ee50269-23e8-499e-9a16-09f393d7600c",
+ "isActive": false,
+ "balance": "$342.52",
+ "age": 27,
+ "firstName": "Isabel",
+ "lastName": "Battle",
+ "company": "EZENTIA",
+ "email": "isabel.battle@ezentia.de",
+ "registered": "Thursday, June 7, 2018 12:16 AM",
+ "latitude": "-53.318152",
+ "longitude": "-153.516824"
+ },
+ {
+ "_id": "5c0e18398d7fb9a4eceeffa2",
+ "index": 8,
+ "guid": "1e30c9ac-2297-4f16-83e6-9559b1ebe92c",
+ "isActive": true,
+ "balance": "$3,184.71",
+ "age": 36,
+ "firstName": "Lenora",
+ "lastName": "Crawford",
+ "company": "KIDGREASE",
+ "email": "lenora.crawford@kidgrease.de",
+ "registered": "Saturday, January 7, 2017 6:17 PM",
+ "latitude": "-72.431496",
+ "longitude": "9.413359"
+ },
+ {
+ "_id": "5c0e18395837069ab6b79d00",
+ "index": 9,
+ "guid": "d04a02ed-5899-4729-a7e5-2d85b5d03973",
+ "isActive": true,
+ "balance": "$1,553.28",
+ "age": 35,
+ "firstName": "Sasha",
+ "lastName": "Bridges",
+ "company": "IDEALIS",
+ "email": "sasha.bridges@idealis.de",
+ "registered": "Sunday, February 4, 2018 7:02 PM",
+ "latitude": "8.095691",
+ "longitude": "-105.758195"
+ },
+ {
+ "_id": "5c0e18390be19bf65acad180",
+ "index": 10,
+ "guid": "3a1a77e6-ef15-4598-8274-c68ac3bb922a",
+ "isActive": false,
+ "balance": "$3,587.96",
+ "age": 20,
+ "firstName": "Wilkins",
+ "lastName": "Beasley",
+ "company": "DIGIFAD",
+ "email": "wilkins.beasley@digifad.de",
+ "registered": "Monday, March 5, 2018 1:27 PM",
+ "latitude": "-88.062704",
+ "longitude": "149.95661"
+ },
+ {
+ "_id": "5c0e1839ffbbad5c9954e49f",
+ "index": 11,
+ "guid": "97a56950-a08c-4e00-8002-ba2d5de4da5d",
+ "isActive": false,
+ "balance": "$1,997.80",
+ "age": 31,
+ "firstName": "Sullivan",
+ "lastName": "Mcclain",
+ "company": "EARTHMARK",
+ "email": "sullivan.mcclain@earthmark.de",
+ "registered": "Saturday, October 27, 2018 2:51 PM",
+ "latitude": "-81.86349",
+ "longitude": "-79.596991"
+ },
+ {
+ "_id": "5c0e183914bd464d55e7325f",
+ "index": 12,
+ "guid": "294f6485-d0f9-4b25-b998-325ae90fa769",
+ "isActive": true,
+ "balance": "$1,405.46",
+ "age": 24,
+ "firstName": "Herminia",
+ "lastName": "Fischer",
+ "company": "ECOLIGHT",
+ "email": "herminia.fischer@ecolight.de",
+ "registered": "Thursday, January 16, 2014 4:48 PM",
+ "latitude": "48.224363",
+ "longitude": "11.08339"
+ },
+ {
+ "_id": "5c0e183968ec2556d8f6566c",
+ "index": 13,
+ "guid": "16edfea4-7b37-4e54-868c-c369b413dd78",
+ "isActive": false,
+ "balance": "$3,440.67",
+ "age": 39,
+ "firstName": "Blanchard",
+ "lastName": "Blackwell",
+ "company": "GEOFORMA",
+ "email": "blanchard.blackwell@geoforma.de",
+ "registered": "Wednesday, July 30, 2014 4:07 AM",
+ "latitude": "-52.169297",
+ "longitude": "10.415879"
+ },
+ {
+ "_id": "5c0e183939a0fc955f2d94da",
+ "index": 14,
+ "guid": "4ed454e2-dde1-4ab5-a434-4a82205ced2d",
+ "isActive": true,
+ "balance": "$1,883.27",
+ "age": 35,
+ "firstName": "Gayle",
+ "lastName": "Little",
+ "company": "AQUAZURE",
+ "email": "gayle.little@aquazure.de",
+ "registered": "Tuesday, December 12, 2017 5:08 PM",
+ "latitude": "-58.473236",
+ "longitude": "38.022269"
+ },
+ {
+ "_id": "5c0e1839099f9221ccd968ac",
+ "index": 15,
+ "guid": "1d052fd4-7c54-45fb-b0db-7de1acc4262a",
+ "isActive": false,
+ "balance": "$2,601.94",
+ "age": 31,
+ "firstName": "Jocelyn",
+ "lastName": "Richards",
+ "company": "GINK",
+ "email": "jocelyn.richards@gink.de",
+ "registered": "Sunday, October 30, 2016 9:12 PM",
+ "latitude": "-43.489676",
+ "longitude": "2.557869"
+ },
+ {
+ "_id": "5c0e183970f320f377321c3f",
+ "index": 16,
+ "guid": "45bca125-8831-48c3-b22b-29ae318e7096",
+ "isActive": false,
+ "balance": "$3,441.74",
+ "age": 34,
+ "firstName": "Berta",
+ "lastName": "Valentine",
+ "company": "ISOSPHERE",
+ "email": "berta.valentine@isosphere.de",
+ "registered": "Sunday, March 19, 2017 8:22 PM",
+ "latitude": "-40.188039",
+ "longitude": "-170.085092"
+ },
+ {
+ "_id": "5c0e1839ab960bb0a9f4f392",
+ "index": 17,
+ "guid": "d7b5122a-94c9-423c-b799-1a8f8314b152",
+ "isActive": false,
+ "balance": "$56.39",
+ "age": 21,
+ "firstName": "Russell",
+ "lastName": "Powers",
+ "company": "TETAK",
+ "email": "russell.powers@tetak.de",
+ "registered": "Thursday, November 3, 2016 9:23 PM",
+ "latitude": "-51.610519",
+ "longitude": "-133.280363"
+ },
+ {
+ "_id": "5c0e183998f0195404b9aaa4",
+ "index": 18,
+ "guid": "a043ba97-ea7e-48ce-bb15-18ee09fb393d",
+ "isActive": true,
+ "balance": "$1,503.57",
+ "age": 37,
+ "firstName": "Rosario",
+ "lastName": "Brennan",
+ "company": "VIAGRAND",
+ "email": "rosario.brennan@viagrand.de",
+ "registered": "Saturday, March 17, 2018 10:32 PM",
+ "latitude": "-43.773365",
+ "longitude": "47.58682"
+ },
+ {
+ "_id": "5c0e1839bcb2a5cc567129ac",
+ "index": 19,
+ "guid": "de6d5d36-201e-4f87-9976-ed31f3160e42",
+ "isActive": false,
+ "balance": "$1,160.18",
+ "age": 29,
+ "firstName": "Anita",
+ "lastName": "Hodges",
+ "company": "TUBALUM",
+ "email": "anita.hodges@tubalum.de",
+ "registered": "Sunday, November 26, 2017 11:54 AM",
+ "latitude": "7.080244",
+ "longitude": "-9.970715"
+ },
+ {
+ "_id": "5c0e18394b37e854a1ef371c",
+ "index": 20,
+ "guid": "9407113b-896a-4699-ac1b-363bc3c6f8ad",
+ "isActive": false,
+ "balance": "$34.81",
+ "age": 31,
+ "firstName": "Barrett",
+ "lastName": "Weaver",
+ "company": "DUOFLEX",
+ "email": "barrett.weaver@duoflex.de",
+ "registered": "Tuesday, November 3, 2015 9:31 AM",
+ "latitude": "40.30558",
+ "longitude": "-69.986664"
+ },
+ {
+ "_id": "5c0e1839b5658f90e16a86e0",
+ "index": 21,
+ "guid": "81f894c4-c931-422d-a30e-593824d95bf9",
+ "isActive": true,
+ "balance": "$2,808.63",
+ "age": 26,
+ "firstName": "Baxter",
+ "lastName": "Chase",
+ "company": "BUNGA",
+ "email": "baxter.chase@bunga.de",
+ "registered": "Friday, October 28, 2016 7:10 AM",
+ "latitude": "-49.05652",
+ "longitude": "63.123535"
+ },
+ {
+ "_id": "5c0e1839cb9462c9ecbb59af",
+ "index": 22,
+ "guid": "92e67862-4fdf-43af-a3ef-ef3edb8d6706",
+ "isActive": true,
+ "balance": "$3,552.71",
+ "age": 29,
+ "firstName": "Olga",
+ "lastName": "Kemp",
+ "company": "OHMNET",
+ "email": "olga.kemp@ohmnet.de",
+ "registered": "Saturday, March 26, 2016 11:51 AM",
+ "latitude": "-17.450481",
+ "longitude": "-13.945794"
+ },
+ {
+ "_id": "5c0e18396f999c2b8ac731a9",
+ "index": 23,
+ "guid": "a682eaae-34f0-4973-b8a0-30972de0732b",
+ "isActive": false,
+ "balance": "$1,999.20",
+ "age": 21,
+ "firstName": "Ebony",
+ "lastName": "Le",
+ "company": "MULTRON",
+ "email": "ebony.le@multron.de",
+ "registered": "Friday, March 27, 2015 9:23 AM",
+ "latitude": "-70.380014",
+ "longitude": "173.20685"
+ },
+ {
+ "_id": "5c0e18391cfb28263eb42db7",
+ "index": 24,
+ "guid": "f1cddb5f-0b89-453e-b0c9-8193a56cc610",
+ "isActive": true,
+ "balance": "$2,950.91",
+ "age": 30,
+ "firstName": "Norman",
+ "lastName": "Price",
+ "company": "COMVEX",
+ "email": "norman.price@comvex.de",
+ "registered": "Tuesday, August 21, 2018 11:17 PM",
+ "latitude": "86.501469",
+ "longitude": "159.545352"
+ },
+ {
+ "_id": "5c0e18394a6be11128c7e5ca",
+ "index": 25,
+ "guid": "dadb738a-40fd-45b6-abac-023a803d95c2",
+ "isActive": true,
+ "balance": "$2,767.09",
+ "age": 25,
+ "firstName": "Sara",
+ "lastName": "Ruiz",
+ "company": "AUSTECH",
+ "email": "sara.ruiz@austech.de",
+ "registered": "Wednesday, June 20, 2018 6:34 AM",
+ "latitude": "86.784904",
+ "longitude": "-120.331325"
+ },
+ {
+ "_id": "5c0e183974631549eda97cea",
+ "index": 26,
+ "guid": "b5c43ee5-14ed-4ab5-b3db-b31a8bb65ceb",
+ "isActive": true,
+ "balance": "$3,235.42",
+ "age": 32,
+ "firstName": "Holly",
+ "lastName": "Santos",
+ "company": "LOVEPAD",
+ "email": "holly.santos@lovepad.de",
+ "registered": "Thursday, November 22, 2018 9:26 PM",
+ "latitude": "-19.640066",
+ "longitude": "50.410992"
+ },
+ {
+ "_id": "5c0e1839ab9b933881429d78",
+ "index": 27,
+ "guid": "94961092-65ca-41b9-bc69-3e40ce2cafc9",
+ "isActive": true,
+ "balance": "$2,106.34",
+ "age": 39,
+ "firstName": "Rachel",
+ "lastName": "Douglas",
+ "company": "DEMINIMUM",
+ "email": "rachel.douglas@deminimum.de",
+ "registered": "Sunday, April 9, 2017 3:55 AM",
+ "latitude": "31.395281",
+ "longitude": "-1.899514"
+ },
+ {
+ "_id": "5c0e183937f743155859c5a9",
+ "index": 28,
+ "guid": "07d7ef18-bcef-483d-999e-0b3da4a7098b",
+ "isActive": true,
+ "balance": "$2,260.65",
+ "age": 40,
+ "firstName": "Reed",
+ "lastName": "Workman",
+ "company": "BUZZMAKER",
+ "email": "reed.workman@buzzmaker.de",
+ "registered": "Wednesday, May 28, 2014 3:44 PM",
+ "latitude": "23.789646",
+ "longitude": "106.938375"
+ },
+ {
+ "_id": "5c0e1839f8f4b60beb28b7ed",
+ "index": 29,
+ "guid": "9b4952e5-aa0e-4919-9e17-7c357a297394",
+ "isActive": false,
+ "balance": "$702.99",
+ "age": 27,
+ "firstName": "Cochran",
+ "lastName": "Ware",
+ "company": "HIVEDOM",
+ "email": "cochran.ware@hivedom.de",
+ "registered": "Monday, October 16, 2017 5:51 AM",
+ "latitude": "85.953108",
+ "longitude": "124.590037"
+ },
+ {
+ "_id": "5c0e1839342fbd54a88269df",
+ "index": 30,
+ "guid": "30937d5b-9514-4ebd-b628-2cfb5017fe41",
+ "isActive": false,
+ "balance": "$385.88",
+ "age": 35,
+ "firstName": "Cote",
+ "lastName": "Hess",
+ "company": "TERAPRENE",
+ "email": "cote.hess@teraprene.de",
+ "registered": "Thursday, March 15, 2018 4:42 PM",
+ "latitude": "81.38211",
+ "longitude": "64.516797"
+ },
+ {
+ "_id": "5c0e18395b6dc85d73ce1fb3",
+ "index": 31,
+ "guid": "f34847da-7f96-4cd8-8d8a-b06c0eb0a8f2",
+ "isActive": true,
+ "balance": "$3,494.56",
+ "age": 27,
+ "firstName": "Daniels",
+ "lastName": "Ayala",
+ "company": "BESTO",
+ "email": "daniels.ayala@besto.de",
+ "registered": "Sunday, December 18, 2016 10:52 AM",
+ "latitude": "47.704227",
+ "longitude": "41.674767"
+ },
+ {
+ "_id": "5c0e183974587cdccf30b13f",
+ "index": 32,
+ "guid": "fdbb6d83-0e47-4453-b8a7-b47f44e4164b",
+ "isActive": false,
+ "balance": "$2,087.38",
+ "age": 26,
+ "firstName": "Powers",
+ "lastName": "Drake",
+ "company": "GENESYNK",
+ "email": "powers.drake@genesynk.de",
+ "registered": "Saturday, September 29, 2018 12:24 AM",
+ "latitude": "40.580432",
+ "longitude": "110.940759"
+ },
+ {
+ "_id": "5c0e18397b51245e971c58b8",
+ "index": 33,
+ "guid": "6adfe544-238b-4001-b2a6-f50ea3094da3",
+ "isActive": true,
+ "balance": "$3,566.22",
+ "age": 34,
+ "firstName": "Pacheco",
+ "lastName": "Ramsey",
+ "company": "ENVIRE",
+ "email": "pacheco.ramsey@envire.de",
+ "registered": "Friday, September 11, 2015 12:14 AM",
+ "latitude": "-30.691235",
+ "longitude": "69.343692"
+ },
+ {
+ "_id": "5c0e18391ede9c0996fd09e7",
+ "index": 34,
+ "guid": "d190b32f-d33b-4c17-a18a-bb2f57e79ba7",
+ "isActive": false,
+ "balance": "$1,671.63",
+ "age": 32,
+ "firstName": "Mcintyre",
+ "lastName": "Chan",
+ "company": "ORBAXTER",
+ "email": "mcintyre.chan@orbaxter.de",
+ "registered": "Wednesday, May 7, 2014 7:11 PM",
+ "latitude": "7.380435",
+ "longitude": "70.955103"
+ },
+ {
+ "_id": "5c0e1839fe48069c9c260fa9",
+ "index": 35,
+ "guid": "a41c064b-6bf4-4ba5-b229-9b657d286936",
+ "isActive": false,
+ "balance": "$24.02",
+ "age": 27,
+ "firstName": "Genevieve",
+ "lastName": "Sparks",
+ "company": "ZBOO",
+ "email": "genevieve.sparks@zboo.de",
+ "registered": "Saturday, December 16, 2017 2:51 PM",
+ "latitude": "-63.406337",
+ "longitude": "118.662621"
+ },
+ {
+ "_id": "5c0e1839a7e8e76accf0803e",
+ "index": 36,
+ "guid": "3e71864d-4be5-418e-ace8-346c3d7a9c5f",
+ "isActive": true,
+ "balance": "$3,261.01",
+ "age": 30,
+ "firstName": "Powell",
+ "lastName": "Patterson",
+ "company": "GAZAK",
+ "email": "powell.patterson@gazak.de",
+ "registered": "Thursday, May 18, 2017 10:10 AM",
+ "latitude": "-10.428548",
+ "longitude": "64.979192"
+ },
+ {
+ "_id": "5c0e183984b0320f1118a8b0",
+ "index": 37,
+ "guid": "ec5b292c-6efb-471b-9bf5-a47286e03515",
+ "isActive": false,
+ "balance": "$918.71",
+ "age": 37,
+ "firstName": "Tara",
+ "lastName": "Mcmillan",
+ "company": "GRAINSPOT",
+ "email": "tara.mcmillan@grainspot.de",
+ "registered": "Sunday, May 17, 2015 1:01 PM",
+ "latitude": "-13.519031",
+ "longitude": "67.931062"
+ },
+ {
+ "_id": "5c0e183965875876835ccd79",
+ "index": 38,
+ "guid": "b7e97ffb-439a-4454-90af-7f5ebd565ebc",
+ "isActive": true,
+ "balance": "$574.99",
+ "age": 28,
+ "firstName": "Pennington",
+ "lastName": "Gallegos",
+ "company": "CEDWARD",
+ "email": "pennington.gallegos@cedward.de",
+ "registered": "Wednesday, September 26, 2018 6:01 AM",
+ "latitude": "-63.693261",
+ "longitude": "-38.352153"
+ },
+ {
+ "_id": "5c0e183922505dd21be49009",
+ "index": 39,
+ "guid": "5187aa39-4357-462b-9508-3c537d26d70d",
+ "isActive": false,
+ "balance": "$2,447.08",
+ "age": 26,
+ "firstName": "Meagan",
+ "lastName": "Irwin",
+ "company": "SENTIA",
+ "email": "meagan.irwin@sentia.de",
+ "registered": "Saturday, April 2, 2016 4:39 PM",
+ "latitude": "1.051313",
+ "longitude": "-86.168315"
+ },
+ {
+ "_id": "5c0e183900a9f7f896e5b3b1",
+ "index": 40,
+ "guid": "31889843-79e7-4636-9ca1-4eb5cbcb0ae3",
+ "isActive": true,
+ "balance": "$1,992.25",
+ "age": 22,
+ "firstName": "Kelly",
+ "lastName": "Cobb",
+ "company": "BOVIS",
+ "email": "kelly.cobb@bovis.de",
+ "registered": "Tuesday, August 9, 2016 5:36 PM",
+ "latitude": "-85.547579",
+ "longitude": "-89.794104"
+ },
+ {
+ "_id": "5c0e18393b25b8552ff950e2",
+ "index": 41,
+ "guid": "0bf02edc-ca1b-4cfe-8356-b65881bdca11",
+ "isActive": true,
+ "balance": "$465.96",
+ "age": 27,
+ "firstName": "Angela",
+ "lastName": "Booker",
+ "company": "EQUICOM",
+ "email": "angela.booker@equicom.de",
+ "registered": "Thursday, July 30, 2015 1:39 AM",
+ "latitude": "-9.345395",
+ "longitude": "107.070665"
+ },
+ {
+ "_id": "5c0e183955d747ebbe25437b",
+ "index": 42,
+ "guid": "6405e559-5849-4d12-ae4e-520f13b4dffe",
+ "isActive": true,
+ "balance": "$15.63",
+ "age": 28,
+ "firstName": "Carrie",
+ "lastName": "Mclean",
+ "company": "BOINK",
+ "email": "carrie.mclean@boink.de",
+ "registered": "Wednesday, February 1, 2017 1:50 PM",
+ "latitude": "72.287519",
+ "longitude": "-135.436286"
+ },
+ {
+ "_id": "5c0e1839e9cfe1b28e31e7e6",
+ "index": 43,
+ "guid": "e49e7ca7-a6cc-4cdb-bebe-5a3b6ba931eb",
+ "isActive": true,
+ "balance": "$3,127.94",
+ "age": 33,
+ "firstName": "Callie",
+ "lastName": "Cooley",
+ "company": "MUSIX",
+ "email": "callie.cooley@musix.de",
+ "registered": "Wednesday, August 30, 2017 4:58 PM",
+ "latitude": "-38.954739",
+ "longitude": "-152.706424"
+ },
+ {
+ "_id": "5c0e18391bafa0750ff4f280",
+ "index": 44,
+ "guid": "c245ffd3-4924-4dce-ae4a-f4cabf057b54",
+ "isActive": false,
+ "balance": "$1,320.36",
+ "age": 35,
+ "firstName": "Terry",
+ "lastName": "Bennett",
+ "company": "EXOTECHNO",
+ "email": "terry.bennett@exotechno.de",
+ "registered": "Friday, June 17, 2016 11:54 PM",
+ "latitude": "-48.946183",
+ "longitude": "32.53167"
+ },
+ {
+ "_id": "5c0e1839e91b27fcce34b70f",
+ "index": 45,
+ "guid": "0860cb66-de4c-410e-8233-aeef5ee9d64e",
+ "isActive": false,
+ "balance": "$1,187.75",
+ "age": 30,
+ "firstName": "Phoebe",
+ "lastName": "Bartlett",
+ "company": "VORATAK",
+ "email": "phoebe.bartlett@voratak.de",
+ "registered": "Tuesday, July 25, 2017 2:57 AM",
+ "latitude": "-63.208957",
+ "longitude": "-91.209743"
+ },
+ {
+ "_id": "5c0e183987e8a4e98415c8dd",
+ "index": 46,
+ "guid": "49219833-172c-4659-9192-d1116a5ca833",
+ "isActive": false,
+ "balance": "$3,225.24",
+ "age": 38,
+ "firstName": "Jordan",
+ "lastName": "Evans",
+ "company": "PHARMACON",
+ "email": "jordan.evans@pharmacon.de",
+ "registered": "Sunday, April 23, 2017 6:27 PM",
+ "latitude": "-59.454678",
+ "longitude": "67.251185"
+ },
+ {
+ "_id": "5c0e183944979692cc1a3e48",
+ "index": 47,
+ "guid": "680c4d15-d539-4db9-8793-a2f6d3f354aa",
+ "isActive": false,
+ "balance": "$2,913.14",
+ "age": 28,
+ "firstName": "Goodman",
+ "lastName": "Cain",
+ "company": "CAXT",
+ "email": "goodman.cain@caxt.de",
+ "registered": "Tuesday, November 1, 2016 6:11 PM",
+ "latitude": "-30.187547",
+ "longitude": "-164.313273"
+ },
+ {
+ "_id": "5c0e1839ef5312ac08e3cbc3",
+ "index": 48,
+ "guid": "85f5fa5d-b6b3-47c6-ad1b-faee10a4e1bd",
+ "isActive": true,
+ "balance": "$544.97",
+ "age": 27,
+ "firstName": "Aisha",
+ "lastName": "Oliver",
+ "company": "MINGA",
+ "email": "aisha.oliver@minga.de",
+ "registered": "Sunday, July 3, 2016 8:18 AM",
+ "latitude": "-21.527536",
+ "longitude": "141.029691"
+ },
+ {
+ "_id": "5c0e1839c2e58f5da04f29fd",
+ "index": 49,
+ "guid": "e2ee9b25-5887-49a9-a1c6-17432154d266",
+ "isActive": true,
+ "balance": "$3,621.65",
+ "age": 31,
+ "firstName": "Erin",
+ "lastName": "Lester",
+ "company": "SLOFAST",
+ "email": "erin.lester@slofast.de",
+ "registered": "Saturday, February 20, 2016 5:13 AM",
+ "latitude": "-30.080798",
+ "longitude": "-1.291093"
+ }
+];
+
+const components = {
+ 'counter': 'demoApp.counter'
+};
+
+type TreeDemoItem = TreeItem<string>;
+
+const treeData: TreeDemoItem[] = [
+ {
+ content: "Erste Ebene", children: [
+ {
+ content: "Zweite Ebene", children: [
+ { content: "Dritte Ebene" },
+ ]
+ },
+ { content: "Zweite Ebene 2" },
+ ]
+ },
+ { content: "Erste Ebene 3" },
+];
+
+const SampleDataMaterialTable = MaterialTable as MaterialTableCtorType<SampleData>;
+
+const SampleTree = TreeView as any as TreeViewCtorType<string>;
+
+const TestComponent = (props: WithComponents<typeof components> & WithStyles<typeof styles>) => {
+
+ return (
+ <div>
+ <h2>About</h2>
+ <Accordion>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography className={props.classes.heading}>Client Side Table Demo</Typography>
+ </AccordionSummary>
+ <AccordionDetails>
+ <SampleDataMaterialTable rows={tableData} tableId={null} columns={
+ [
+ { property: "index", type: ColumnType.text, title: "Index", width: "80px", disableFilter: true, disableSorting: true },
+ { property: "firstName", type: ColumnType.text, title: "First Name" },
+ { property: "lastName", type: ColumnType.text, title: "Last Name" },
+ { property: "age", type: ColumnType.numeric, title: "Age", width: "60px" },
+ { property: "email", type: ColumnType.text, title: "eMail" },
+ { property: "actions", type: ColumnType.custom, title: "Actions", customControl: ({ rowData }) => (<div>Button</div>) },
+ ]
+ } idProperty={"_id"} title={"Customers 2018"} >
+ </SampleDataMaterialTable>
+ </AccordionDetails>
+ </Accordion>
+ <Accordion>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography className={props.classes.heading}>Tree Demo</Typography>
+ </AccordionSummary>
+ <AccordionDetails>
+ <SampleTree items={treeData} useFolderIcons enableSearchBar />
+ </AccordionDetails>
+ </Accordion>
+ </div>
+ );
+};
+
+export const Test = withComponents(components)(withStyles(styles)(TestComponent));
+export default Test;