From 15e2d3a29b0d1a304965e34f114a911e5a7abdb3 Mon Sep 17 00:00:00 2001 From: sai-neetha Date: Mon, 20 Mar 2023 08:05:47 +0100 Subject: Odlux Update Add eslint and custom icons update Issue-ID: CCSDK-3871 Signed-off-by: sai-neetha Change-Id: If6b676128cc9cff0437a5dc54f85eaafd3b8c586 Signed-off-by: highstreetherbert --- sdnr/wt/odlux/framework/pom.xml | 5 +- .../odlux/framework/src/actions/settingsAction.ts | 8 +- sdnr/wt/odlux/framework/src/app.tsx | 7 +- sdnr/wt/odlux/framework/src/assets/icons/About.svg | 18 ++ sdnr/wt/odlux/framework/src/assets/icons/Home.svg | 28 ++ sdnr/wt/odlux/framework/src/assets/icons/Menu.svg | 18 ++ sdnr/wt/odlux/framework/src/assets/icons/Tools.svg | 35 +++ sdnr/wt/odlux/framework/src/assets/icons/User.svg | 21 ++ .../framework/src/assets/icons/ht.Connect.png | Bin 0 -> 14989 bytes .../framework/src/assets/icons/ht.Connect.svg | 81 ++++++ .../framework/src/components/errorDisplay.tsx | 4 +- .../framework/src/components/icons/menuIcon.tsx | 29 ++ sdnr/wt/odlux/framework/src/components/logo.tsx | 4 +- .../src/components/material-table/index.tsx | 16 +- .../components/material-table/showColumnDialog.tsx | 6 +- .../src/components/material-table/utilities.ts | 9 +- .../src/components/material-ui/listItemLink.tsx | 14 +- .../framework/src/components/navigationMenu.tsx | 27 +- .../framework/src/components/routing/appFrame.tsx | 4 +- .../framework/src/components/settings/general.tsx | 4 +- .../wt/odlux/framework/src/components/titleBar.tsx | 27 +- sdnr/wt/odlux/framework/src/flux/connect.ts | 161 ----------- sdnr/wt/odlux/framework/src/flux/connect.tsx | 213 ++++++++++++++ sdnr/wt/odlux/framework/src/flux/store.ts | 16 +- .../src/handlers/authenticationHandler.ts | 2 +- sdnr/wt/odlux/framework/src/middleware/logger.ts | 7 +- .../odlux/framework/src/middleware/navigation.ts | 2 +- .../odlux/framework/src/services/applicationApi.ts | 5 +- .../framework/src/services/applicationManager.ts | 4 +- .../framework/src/services/broadcastService.ts | 146 +++++----- sdnr/wt/odlux/framework/src/services/index.ts | 2 +- .../wt/odlux/framework/src/services/restService.ts | 54 ++-- .../framework/src/services/settingsService.ts | 41 --- .../odlux/framework/src/services/storeService.ts | 11 + .../framework/src/services/userdataService.ts | 41 +++ .../odlux/framework/src/store/applicationStore.ts | 6 +- sdnr/wt/odlux/framework/src/utilities/logLevel.ts | 8 + sdnr/wt/odlux/framework/src/views/about.tsx | 78 +++-- sdnr/wt/odlux/framework/src/views/frame.tsx | 143 +++++---- sdnr/wt/odlux/framework/src/views/home.tsx | 33 +-- sdnr/wt/odlux/framework/src/views/login.tsx | 320 +++++++++------------ sdnr/wt/odlux/framework/src/views/settings.tsx | 45 ++- .../framework/src2/main/resources/version.json | 2 + 43 files changed, 983 insertions(+), 722 deletions(-) create mode 100644 sdnr/wt/odlux/framework/src/assets/icons/About.svg create mode 100644 sdnr/wt/odlux/framework/src/assets/icons/Home.svg create mode 100644 sdnr/wt/odlux/framework/src/assets/icons/Menu.svg create mode 100644 sdnr/wt/odlux/framework/src/assets/icons/Tools.svg create mode 100644 sdnr/wt/odlux/framework/src/assets/icons/User.svg create mode 100644 sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.png create mode 100644 sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.svg create mode 100644 sdnr/wt/odlux/framework/src/components/icons/menuIcon.tsx delete mode 100644 sdnr/wt/odlux/framework/src/flux/connect.ts create mode 100644 sdnr/wt/odlux/framework/src/flux/connect.tsx delete mode 100644 sdnr/wt/odlux/framework/src/services/settingsService.ts create mode 100644 sdnr/wt/odlux/framework/src/services/storeService.ts create mode 100644 sdnr/wt/odlux/framework/src/services/userdataService.ts create mode 100644 sdnr/wt/odlux/framework/src/utilities/logLevel.ts (limited to 'sdnr/wt/odlux/framework') diff --git a/sdnr/wt/odlux/framework/pom.xml b/sdnr/wt/odlux/framework/pom.xml index 414f49827..3dd0f1a9d 100644 --- a/sdnr/wt/odlux/framework/pom.xml +++ b/sdnr/wt/odlux/framework/pom.xml @@ -19,6 +19,7 @@ ~ ============LICENSE_END======================================================= ~ --> + 4.0.0 @@ -45,7 +46,7 @@ ${maven.build.timestamp} ONAP Frankfurt (Neon, mdsal ${odl.mdsal.version}) - 142.63ceae1(22/01/31) + 178.3bd8a2a9(23/03/16) ONAP SDN-R | ONF Wireless for ${distversion} - Build: ${buildtime} ${buildno} ${project.version} @@ -102,7 +103,7 @@ initialize - v12.13.0 + v12.22.0 v1.22.10 diff --git a/sdnr/wt/odlux/framework/src/actions/settingsAction.ts b/sdnr/wt/odlux/framework/src/actions/settingsAction.ts index 1c18ddc8e..092b31814 100644 --- a/sdnr/wt/odlux/framework/src/actions/settingsAction.ts +++ b/sdnr/wt/odlux/framework/src/actions/settingsAction.ts @@ -19,7 +19,7 @@ import { Dispatch } from "../flux/store"; import { Action } from "../flux/action"; import { GeneralSettings, Settings, TableSettings, TableSettingsColumn } from "../models/settings"; -import { getSettings, putSettings } from "../services/settingsService"; +import { getUserdata, saveUserdata } from "../services/userdataService"; import { startWebsocketSession, suspendWebsocketSession } from "../services/notificationService"; import { IApplicationStoreState } from "../store/applicationStore"; @@ -67,7 +67,7 @@ export const setGeneralSettingsAction = (value: boolean) => (dispatcher: Dispatc export const updateGeneralSettingsAction = (activateNotifications: boolean) => async (dispatcher: Dispatch) => { const value: GeneralSettings = { general: { areNotificationsEnabled: activateNotifications } }; - const result = await putSettings("/general", JSON.stringify(value.general)); + const result = await saveUserdata("/general", JSON.stringify(value.general)); dispatcher(setGeneralSettingsAction(activateNotifications)); } @@ -86,14 +86,14 @@ export const updateTableSettings = (tableName: string, columns: TableSettingsCol // would only save latest entry //const json = JSON.stringify({ [tableName]: { columns: columns } }); - const result = await putSettings("/tables", json); + const result = await saveUserdata("/tables", json); dispatcher(new SetTableSettings(tableName, columns)); } export const getGeneralSettingsAction = () => async (dispatcher: Dispatch) => { - const result = await getSettings(); + const result = await getUserdata(); if (result && result.general) { dispatcher(new SetGeneralSettingsAction(result.general.areNotificationsEnabled!)) diff --git a/sdnr/wt/odlux/framework/src/app.tsx b/sdnr/wt/odlux/framework/src/app.tsx index 9b03a216d..bbe1f9ec8 100644 --- a/sdnr/wt/odlux/framework/src/app.tsx +++ b/sdnr/wt/odlux/framework/src/app.tsx @@ -32,14 +32,15 @@ import { applicationStoreCreator } from './store/applicationStore'; import { ApplicationStoreProvider } from './flux/connect'; import { startHistoryListener } from './middleware/navigation'; -import { startRestService } from './services/restService'; +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'; -import { startBroadcastChannel } from './services/broadcastService'; declare module '@mui/material/styles' { @@ -98,7 +99,7 @@ export const runApplication = () => { }; - startRestService(applicationStore); + startSoreService(applicationStore); startHistoryListener(applicationStore); startNotificationService(applicationStore); diff --git a/sdnr/wt/odlux/framework/src/assets/icons/About.svg b/sdnr/wt/odlux/framework/src/assets/icons/About.svg new file mode 100644 index 000000000..156e36efe --- /dev/null +++ b/sdnr/wt/odlux/framework/src/assets/icons/About.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/sdnr/wt/odlux/framework/src/assets/icons/Home.svg b/sdnr/wt/odlux/framework/src/assets/icons/Home.svg new file mode 100644 index 000000000..0836714b4 --- /dev/null +++ b/sdnr/wt/odlux/framework/src/assets/icons/Home.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + diff --git a/sdnr/wt/odlux/framework/src/assets/icons/Menu.svg b/sdnr/wt/odlux/framework/src/assets/icons/Menu.svg new file mode 100644 index 000000000..ea0312802 --- /dev/null +++ b/sdnr/wt/odlux/framework/src/assets/icons/Menu.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/sdnr/wt/odlux/framework/src/assets/icons/Tools.svg b/sdnr/wt/odlux/framework/src/assets/icons/Tools.svg new file mode 100644 index 000000000..1595cdc1c --- /dev/null +++ b/sdnr/wt/odlux/framework/src/assets/icons/Tools.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + diff --git a/sdnr/wt/odlux/framework/src/assets/icons/User.svg b/sdnr/wt/odlux/framework/src/assets/icons/User.svg new file mode 100644 index 000000000..99618cf57 --- /dev/null +++ b/sdnr/wt/odlux/framework/src/assets/icons/User.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.png b/sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.png new file mode 100644 index 000000000..412390c79 Binary files /dev/null and b/sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.png differ diff --git a/sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.svg b/sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.svg new file mode 100644 index 000000000..c1d74bc96 --- /dev/null +++ b/sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.svg @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sdnr/wt/odlux/framework/src/components/errorDisplay.tsx b/sdnr/wt/odlux/framework/src/components/errorDisplay.tsx index a04ab16af..d41d82687 100644 --- a/sdnr/wt/odlux/framework/src/components/errorDisplay.tsx +++ b/sdnr/wt/odlux/framework/src/components/errorDisplay.tsx @@ -15,7 +15,7 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import React from 'react'; import { Theme } from '@mui/material/styles'; import { WithStyles } from '@mui/styles'; @@ -31,7 +31,7 @@ import Typography from '@mui/material/Typography'; import { ClearErrorInfoAction, RemoveErrorInfoAction } from '../actions/errorActions'; -import connect, { Connect } from '../flux/connect'; +import { connect, Connect } from '../flux/connect'; const styles = (theme: Theme) => createStyles({ modal: { diff --git a/sdnr/wt/odlux/framework/src/components/icons/menuIcon.tsx b/sdnr/wt/odlux/framework/src/components/icons/menuIcon.tsx new file mode 100644 index 000000000..0d7d734c9 --- /dev/null +++ b/sdnr/wt/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 ( + + + + + + ); +}; + +MenuIcon.defaultName = 'MenuIcon'; + +export default MenuIcon; \ No newline at end of file diff --git a/sdnr/wt/odlux/framework/src/components/logo.tsx b/sdnr/wt/odlux/framework/src/components/logo.tsx index b10cc8ce1..f2bb1f575 100644 --- a/sdnr/wt/odlux/framework/src/components/logo.tsx +++ b/sdnr/wt/odlux/framework/src/components/logo.tsx @@ -42,7 +42,7 @@ import withStyles from '@mui/styles/withStyles'; import createStyles from '@mui/styles/createStyles'; -import defaultLogo from '../assets/images/defaultLogo.svg'; +const defaultLogo = require('../assets/icons/ht.Connect.svg'); const styles = (theme: Theme) => createStyles({ headerLogo: { @@ -91,7 +91,7 @@ class LogoComponent extends React.Component { console.info([ "Logo hidden, because browser window width (", this.state.windowWidth, - "px) is lower thershold (", + "px) is lower threshold (", this.hideLogoWhenWindowWidthIsLower, "px)."].join('')); } diff --git a/sdnr/wt/odlux/framework/src/components/material-table/index.tsx b/sdnr/wt/odlux/framework/src/components/material-table/index.tsx index 8541cfe56..c1a5005d4 100644 --- a/sdnr/wt/odlux/framework/src/components/material-table/index.tsx +++ b/sdnr/wt/odlux/framework/src/components/material-table/index.tsx @@ -450,13 +450,13 @@ class MaterialTableComponent extends React.Component=')) { - return valueAsNumber >= Number(filterExpressionAsString.substr(2).trim()); + return valueAsNumber >= Number(filterExpressionAsString.substring(2).trim()); } else if (filterExpressionAsString.startsWith('<=')) { - return valueAsNumber <= Number(filterExpressionAsString.substr(2).trim()); + return valueAsNumber <= Number(filterExpressionAsString.substring(2).trim()); } else if (filterExpressionAsString.startsWith('>')) { - return valueAsNumber > Number(filterExpressionAsString.substr(1).trim()); + return valueAsNumber > Number(filterExpressionAsString.substring(1).trim()); } else if (filterExpressionAsString.startsWith('<')) { - return valueAsNumber < Number(filterExpressionAsString.substr(1).trim()); + return valueAsNumber < Number(filterExpressionAsString.substring(1).trim()); } } else if (column.type === ColumnType.date){ const valueAsString = String(dataValue); @@ -480,13 +480,13 @@ class MaterialTableComponent extends React.Component=')) { - return valueAsDate >= convertToDate(filterExpressionAsString.substr(2).trim()); + return valueAsDate >= convertToDate(filterExpressionAsString.substring(2).trim()); } else if (filterExpressionAsString.startsWith('<=')) { - return valueAsDate <= convertToDate(filterExpressionAsString.substr(2).trim()); + return valueAsDate <= convertToDate(filterExpressionAsString.substring(2).trim()); } else if (filterExpressionAsString.startsWith('>')) { - return valueAsDate > convertToDate(filterExpressionAsString.substr(1).trim()); + return valueAsDate > convertToDate(filterExpressionAsString.substring(1).trim()); } else if (filterExpressionAsString.startsWith('<')) { - return valueAsDate < convertToDate(filterExpressionAsString.substr(1).trim()); + return valueAsDate < convertToDate(filterExpressionAsString.substring(1).trim()); } diff --git a/sdnr/wt/odlux/framework/src/components/material-table/showColumnDialog.tsx b/sdnr/wt/odlux/framework/src/components/material-table/showColumnDialog.tsx index f8ae6ea97..ab0d465e7 100644 --- a/sdnr/wt/odlux/framework/src/components/material-table/showColumnDialog.tsx +++ b/sdnr/wt/odlux/framework/src/components/material-table/showColumnDialog.tsx @@ -16,9 +16,9 @@ * ============LICENSE_END========================================================================== */ -import { Button, Checkbox, FormControlLabel, MenuItem, Popover, Switch, Typography } from '@mui/material'; -import connect, { Connect, IDispatcher } from '../../flux/connect'; -import * as React from 'react'; +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'; diff --git a/sdnr/wt/odlux/framework/src/components/material-table/utilities.ts b/sdnr/wt/odlux/framework/src/components/material-table/utilities.ts index f9015493f..e2fda7647 100644 --- a/sdnr/wt/odlux/framework/src/components/material-table/utilities.ts +++ b/sdnr/wt/odlux/framework/src/components/material-table/utilities.ts @@ -51,6 +51,7 @@ export type ExternalMethodes = { onHandleChangeRowsPerPage: (rowsPerPage: number | null) => void; onHideColumns: (columnName: string[]) => void; onShowColumns: (columnName: string[]) => void; + onClearFilters: () => void; }, createPreActions: (dispatch: Dispatch, skipRefresh?: boolean) => { onPreFilterChanged: (preFilter: { @@ -328,7 +329,13 @@ export function createExternal(callback: DataCallback, selectState dispatch((dispatch: Dispatch) => { dispatch(new ShowColumnsAction(columnName)); }) - } + }, + onClearFilters: () => { + dispatch((dispatch: Dispatch) => { + let filter = { }; + dispatch(new SetFilterChangedAction(filter)); + }); + }, // selected: }; }; diff --git a/sdnr/wt/odlux/framework/src/components/material-ui/listItemLink.tsx b/sdnr/wt/odlux/framework/src/components/material-ui/listItemLink.tsx index 744cb0d24..626cb8978 100644 --- a/sdnr/wt/odlux/framework/src/components/material-ui/listItemLink.tsx +++ b/sdnr/wt/odlux/framework/src/components/material-ui/listItemLink.tsx @@ -27,6 +27,8 @@ 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: { @@ -35,7 +37,7 @@ const styles = (theme: Theme) => createStyles({ }); export interface IListItemLinkProps extends WithStyles { - icon: JSX.Element | null; + icon: IconType | null; primary: string | React.ComponentType; secondary?: React.ComponentType; to: string; @@ -48,13 +50,19 @@ export const ListItemLink = withStyles(styles)((props: IListItemLinkProps) => { const renderLink = (itemProps: any): JSX.Element => ( props.external ? : ); - + + 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' ? : ); + return ( <> { icon - ? { icon } + ? { listItemIcon } : null } { typeof Primary === 'string' diff --git a/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx b/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx index 195706d28..d969286b7 100644 --- a/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx +++ b/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx @@ -15,15 +15,14 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +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 { faHome, faAddressBook } from '@fortawesome/free-solid-svg-icons'; - import Drawer from '@mui/material/Drawer'; import List from '@mui/material/List'; @@ -34,11 +33,14 @@ import { faProjectDiagram } from '@fortawesome/free-solid-svg-icons'; import ListItemLink from '../components/material-ui/listItemLink'; -import connect, { Connect } from '../flux/connect'; +import { connect, Connect } from '../flux/connect'; import { MenuAction } from '../actions/menuAction'; -import * as classNames from 'classnames'; 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; @@ -141,16 +143,15 @@ export const NavigationMenu = withStyles(styles)(connect()(({ classes, state, di window.dispatchEvent(new Event('menu-resized')); }, [isOpen]) - let menuItems = state.framework.applicationRegistraion && Object.keys(state.framework.applicationRegistraion).map(key => { - const reg = state.framework.applicationRegistraion[key]; - const icon = !reg.icon ? null :( typeof reg.icon === 'string' ? : ) + let menuItems = state.framework.applicationRegistration && Object.keys(state.framework.applicationRegistration).map(key => { + const reg = state.framework.applicationRegistration[key]; return reg && ( + icon={reg.icon || null} /> ) || null; }) || null; @@ -160,7 +161,7 @@ export const NavigationMenu = withStyles(styles)(connect()(({ classes, state, di key={"transportPCE"} to={transportUrl} primary={"TransportPCE"} - icon={} + icon={faProjectDiagram} external />; const linkFound = menuItems.find(obj => obj.key === "linkCalculation"); @@ -191,17 +192,17 @@ export const NavigationMenu = withStyles(styles)(connect()(({ classes, state, di
{ /* https://fiffty.github.io/react-treeview-mui/ */} - } /> + { menuItems } - } /> + {(false && process.env.NODE_ENV === "development") ? <> - } /> + : null } diff --git a/sdnr/wt/odlux/framework/src/components/routing/appFrame.tsx b/sdnr/wt/odlux/framework/src/components/routing/appFrame.tsx index d055b8a87..aa22f17f4 100644 --- a/sdnr/wt/odlux/framework/src/components/routing/appFrame.tsx +++ b/sdnr/wt/odlux/framework/src/components/routing/appFrame.tsx @@ -15,9 +15,9 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import React from 'react'; -import connect, { Connect } from '../../flux/connect'; +import { connect, Connect } from '../../flux/connect'; import { SetTitleAction } from '../../actions/titleActions'; import { AddErrorInfoAction } from '../../actions/errorActions'; diff --git a/sdnr/wt/odlux/framework/src/components/settings/general.tsx b/sdnr/wt/odlux/framework/src/components/settings/general.tsx index 90f15c1d2..ffd516ba1 100644 --- a/sdnr/wt/odlux/framework/src/components/settings/general.tsx +++ b/sdnr/wt/odlux/framework/src/components/settings/general.tsx @@ -16,11 +16,11 @@ * ============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 * as React from 'react'; -import connect, { Connect, IDispatcher } from '../../flux/connect'; +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'; diff --git a/sdnr/wt/odlux/framework/src/components/titleBar.tsx b/sdnr/wt/odlux/framework/src/components/titleBar.tsx index 19d3bdf74..40c0fc736 100644 --- a/sdnr/wt/odlux/framework/src/components/titleBar.tsx +++ b/sdnr/wt/odlux/framework/src/components/titleBar.tsx @@ -15,7 +15,7 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import React from 'react'; import { withRouter, RouteComponentProps } from 'react-router-dom'; import { Theme } from '@mui/material/styles'; @@ -27,9 +27,6 @@ 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 Block from '@mui/icons-material/Block'; -import Adjust from '@mui/icons-material/Adjust'; -import MenuIcon from '@mui/icons-material/Menu'; import AccountCircle from '@mui/icons-material/AccountCircle'; import MenuItem from '@mui/material/MenuItem'; import Menu from '@mui/material/Menu'; @@ -41,10 +38,12 @@ 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 Logo from './logo'; +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, @@ -58,7 +57,8 @@ const styles = (theme: Theme) => createStyles({ }, icon: { marginLeft: 16, - marginRight: 8 + marginRight: 8, + marginBottom: -2, }, connected: { color: "green" @@ -112,10 +112,10 @@ class TitleBarComponent extends React.Component { - const reg = state.framework.applicationRegistraion[key]; + Object.keys(state.framework.applicationRegistration).map(key => { + const reg = state.framework.applicationRegistration[key]; if (reg && reg.statusBarElement) { if (key === "help") { isNotificationInfoAdded = true; @@ -132,7 +132,12 @@ class TitleBarComponent extends React.Component : ) + const customIconHeight = 22; + const icon = !stateIcon + ? null + : (typeof stateIcon === 'string' + ? + : ) return ( diff --git a/sdnr/wt/odlux/framework/src/flux/connect.ts b/sdnr/wt/odlux/framework/src/flux/connect.ts deleted file mode 100644 index f54e4e0f0..000000000 --- a/sdnr/wt/odlux/framework/src/flux/connect.ts +++ /dev/null @@ -1,161 +0,0 @@ -/** - * ============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 PropTypes from 'prop-types'; - -import { Dispatch } from '../flux/store'; - -import { ApplicationStore, IApplicationStoreState } from '../store/applicationStore'; - -interface IApplicationStoreContext { - applicationStore: ApplicationStore; -} - -export interface IDispatcher { - dispatch: Dispatch; -} - -interface IApplicationStoreProps { - state: IApplicationStoreState; -} - -interface IDispatchProps { - dispatch: Dispatch; -} - -type Omit = Pick> - -type ComponentDecoratorInfer = { - (wrappedComponent: React.ComponentType): React.ComponentClass>; -}; - -export type Connect any) | undefined = undefined, TMapDispatch extends ((...args: any) => any) | undefined = undefined> = - (TMapProps extends ((...args: any) => any) ? ReturnType : IApplicationStoreProps) & - (TMapDispatch extends ((...args: any) => any) ? ReturnType : IDispatchProps); - -export function connect(): ComponentDecoratorInfer; - -export function connect( - mapStateToProps: (state: IApplicationStoreState) => TStateProps -): ComponentDecoratorInfer; - -export function connect( - mapStateToProps: (state: IApplicationStoreState) => TStateProps, - mapDispatchToProps: (dispatcher: IDispatcher) => TDispatchProps -): ComponentDecoratorInfer; - - -export function connect( - mapStateToProps: undefined, - mapDispatchToProps: (dispatcher: IDispatcher) => TDispatchProps -): ComponentDecoratorInfer; - - -export function connect( - mapStateToProps?: ((state: IApplicationStoreState) => TStateProps), - mapDispatchToProps?: ((dispatcher: IDispatcher) => TDispatchProps) -): - ((WrappedComponent: React.ComponentType) => React.ComponentType) { - - const injectApplicationStore = (WrappedComponent: React.ComponentType): React.ComponentType => { - - class StoreAdapter extends React.Component { - public static contextTypes = { ...WrappedComponent.contextTypes, applicationStore: PropTypes.object.isRequired }; - context: IApplicationStoreContext; - - 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(); - } - } - - return StoreAdapter; - } - - - return injectApplicationStore; - - /* inline methods */ - - function isWrappedComponentIsVersion1(wrappedComponent: any): wrappedComponent is React.ComponentType { - return !mapStateToProps && !mapDispatchToProps; - } - - function isWrappedComponentIsVersion2(wrappedComponent: any): wrappedComponent is React.ComponentType { - return !!mapStateToProps && !mapDispatchToProps; - } - - function isWrappedComponentIsVersion3(wrappedComponent: any): wrappedComponent is React.ComponentType { - return !!mapStateToProps && !!mapDispatchToProps; - } - - function isWrappedComponentIsVersion4(wrappedComponent: any): wrappedComponent is React.ComponentType { - return !mapStateToProps && !!mapDispatchToProps; - } -} - -interface ApplicationStoreProviderProps extends React.Props { - applicationStore: ApplicationStore; -} - -export class ApplicationStoreProvider extends React.Component - implements /* React.ComponentLifecycle, */ React.ChildContextProvider { - - public static childContextTypes = { applicationStore: PropTypes.object.isRequired }; - - getChildContext(): IApplicationStoreContext { - return { - applicationStore: this.props.applicationStore - }; - } - - render(): JSX.Element { - return React.Children.only(this.props.children) as any; //type error, fix when possible - } -} - -export default connect; \ No newline at end of file diff --git a/sdnr/wt/odlux/framework/src/flux/connect.tsx b/sdnr/wt/odlux/framework/src/flux/connect.tsx new file mode 100644 index 000000000..09d30dae7 --- /dev/null +++ b/sdnr/wt/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 = { + (wrappedComponent: React.ComponentType): React.ComponentClass>; +}; + +const ApplicationStoreContext = createContext(undefined); + +export type Connect any) | undefined = undefined, TMapDispatch extends ((...args: any) => any) | undefined = undefined> = + (TMapProps extends ((...args: any) => any) ? ReturnType : IApplicationStoreProps) & + (TMapDispatch extends ((...args: any) => any) ? ReturnType : IDispatchProps); + +export function connect(): ComponentDecoratorInfer; + +export function connect( + mapStateToProps: (state: IApplicationStoreState) => TStateProps +): ComponentDecoratorInfer; + +export function connect( + mapStateToProps: (state: IApplicationStoreState) => TStateProps, + mapDispatchToProps: (dispatcher: IDispatcher) => TDispatchProps +): ComponentDecoratorInfer; + + +export function connect( + mapStateToProps: undefined, + mapDispatchToProps: (dispatcher: IDispatcher) => TDispatchProps +): ComponentDecoratorInfer; + + +export function connect( + mapStateToProps?: ((state: IApplicationStoreState) => TStateProps), + mapDispatchToProps?: ((dispatcher: IDispatcher) => TDispatchProps) +): + ((WrappedComponent: React.ComponentType) => React.ComponentType) { + + const injectApplicationStore = (WrappedComponent: React.ComponentType): React.ComponentType => { + + class StoreAdapter extends React.Component { + + 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 { + return !mapStateToProps && !mapDispatchToProps; + } + + function isWrappedComponentIsVersion2(wrappedComponent: any): wrappedComponent is React.ComponentType { + return !!mapStateToProps && !mapDispatchToProps; + } + + function isWrappedComponentIsVersion3(wrappedComponent: any): wrappedComponent is React.ComponentType { + return !!mapStateToProps && !!mapDispatchToProps; + } + + function isWrappedComponentIsVersion4(wrappedComponent: any): wrappedComponent is React.ComponentType { + return !mapStateToProps && !!mapDispatchToProps; + } +} + +type ApplicationStoreProviderProps = { + applicationStore: ApplicationStore; +} + +export const ApplicationStoreProvider: FC = (props) => { + const { applicationStore, children } = props; + + return ( + + {children} + + ); +}; + +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 = ( 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(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/framework/src/flux/store.ts b/sdnr/wt/odlux/framework/src/flux/store.ts index dd80ce7ba..347d295e0 100644 --- a/sdnr/wt/odlux/framework/src/flux/store.ts +++ b/sdnr/wt/odlux/framework/src/flux/store.ts @@ -20,6 +20,8 @@ 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 { (action: TAction): TAction; } @@ -28,8 +30,8 @@ export interface Enhancer { (store: Store): Dispatch; } -class InitialisationAction extends Action { }; -const initialisationAction = new InitialisationAction(); +class InitializationAction extends Action { }; +const initializationAction = new InitializationAction(); export class Store { @@ -43,19 +45,22 @@ export class Store { this._isDispatching = false; - this.changed = new Event(); // sollten wir hier eventuell sogar den state mit übergeben ? + this.changed = new Event(); this._actionHandler = actionHandler; this._state = initialState as TStoreState; if (enhancer) this._dispatch = enhancer(this); - this._dispatch(initialisationAction); + this._dispatch(initializationAction); } public changed: Event; private _dispatch: Dispatch = (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. ' + @@ -76,6 +81,9 @@ export class Store { } if (this._state !== oldState) { + if (LogLevel > 3) { + console.log('Store::Dispatch - state has changed', this._state); + } this.changed.invoke(); } diff --git a/sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts b/sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts index f98d77487..71b9e33d1 100644 --- a/sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts +++ b/sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts @@ -23,7 +23,7 @@ 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 { getSettings } from '../services/settingsService'; +import { getUserdata } from '../services/userdataService'; export interface IAuthenticationState { user?: User; diff --git a/sdnr/wt/odlux/framework/src/middleware/logger.ts b/sdnr/wt/odlux/framework/src/middleware/logger.ts index 47725e696..fb0874f3e 100644 --- a/sdnr/wt/odlux/framework/src/middleware/logger.ts +++ b/sdnr/wt/odlux/framework/src/middleware/logger.ts @@ -18,16 +18,17 @@ 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 => { - console.log('will dispatch', action); + if (LogLevel > 2) console.log('will dispatch', action); const returnValue = next(action); - console.log('state after dispatch', getState()); + if (LogLevel > 2) console.log('state after dispatch', getState()); return returnValue; }; - } + }; } export const logger = createLoggerMiddleware(); diff --git a/sdnr/wt/odlux/framework/src/middleware/navigation.ts b/sdnr/wt/odlux/framework/src/middleware/navigation.ts index 5f3eed55d..44035fec3 100644 --- a/sdnr/wt/odlux/framework/src/middleware/navigation.ts +++ b/sdnr/wt/odlux/framework/src/middleware/navigation.ts @@ -55,7 +55,7 @@ const routerMiddlewareCreator = (history: History) => () => (next: Dispatch): Di // 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.substr(ind+6) : null; + const tokenStr = ind > -1 ? action.search.substring(ind+6) : null; const token = tokenStr && jwt.decode(tokenStr); if (tokenStr && token) { // @ts-ignore diff --git a/sdnr/wt/odlux/framework/src/services/applicationApi.ts b/sdnr/wt/odlux/framework/src/services/applicationApi.ts index 36523f9eb..8246ee8fa 100644 --- a/sdnr/wt/odlux/framework/src/services/applicationApi.ts +++ b/sdnr/wt/odlux/framework/src/services/applicationApi.ts @@ -15,13 +15,10 @@ * the License. * ============LICENSE_END========================================================================== */ -import { GeneralSettings } from '../models/settings'; -import { setGeneralSettingsAction, SetGeneralSettingsAction } from '../actions/settingsAction'; + import { Event } from '../common/event'; import { ApplicationStore } from '../store/applicationStore'; import { AuthMessage, getBroadcastChannel, sendMessage } from './broadcastService'; -import { endWebsocketSession } from './notificationService'; -import { getSettings } from './settingsService'; let resolveApplicationStoreInitialized: (store: ApplicationStore) => void; let applicationStore: ApplicationStore | null = null; diff --git a/sdnr/wt/odlux/framework/src/services/applicationManager.ts b/sdnr/wt/odlux/framework/src/services/applicationManager.ts index 14e3ed0b2..bd620a020 100644 --- a/sdnr/wt/odlux/framework/src/services/applicationManager.ts +++ b/sdnr/wt/odlux/framework/src/services/applicationManager.ts @@ -23,7 +23,7 @@ import { applicationApi } from './applicationApi'; /** Represents registry to manage all applications. */ class ApplicationManager { - /** Stores all registerd applications. */ + /** Stores all registered applications. */ private _applications: { [key: string]: ApplicationInfo }; /** Initializes a new instance of this class. */ @@ -32,7 +32,7 @@ class ApplicationManager { this.changed = new Event(); } - /** The chaged event will fire if the registration has changed. */ + /** The changed event will fire if the registration has changed. */ public changed: Event; /** Registers a new application. */ diff --git a/sdnr/wt/odlux/framework/src/services/broadcastService.ts b/sdnr/wt/odlux/framework/src/services/broadcastService.ts index f2c3ebc57..40968e54f 100644 --- a/sdnr/wt/odlux/framework/src/services/broadcastService.ts +++ b/sdnr/wt/odlux/framework/src/services/broadcastService.ts @@ -22,89 +22,95 @@ import { ReplaceAction } from "../actions/navigationActions"; import { User } from "../models/authentication"; import { ApplicationStore } from "../store/applicationStore"; -type Broadcaster = {channel: BroadcastChannel, key: String}; +type Broadcaster = { + channel: BroadcastChannel; + key: String; +}; type AuthTypes = 'login' | 'logout'; -export type AuthMessage={key: AuthTypes, data: any}; +export type AuthMessage = { + key: AuthTypes; + data: any; +}; type SettingsType = 'general'; -export type SettingsMessage={key: SettingsType, enableNotifications: boolean, user: string}; +export type SettingsMessage = { + key: SettingsType; + enableNotifications: boolean; + user: string; +}; -let channels: Broadcaster[] = []; -let store : ApplicationStore | null = null; +const channels: Broadcaster[] = []; +let store: ApplicationStore | null = null; export const saveChannel = (channel: BroadcastChannel, channelName: string) => { - channels.push({channel: channel, key: channelName}); -} - -export const startBroadcastChannel = (applicationStore: ApplicationStore)=>{ - store=applicationStore; - - //might decide to use one general broadcast channel with more keys in the future - createAuthBroadcastChannel(); - createSettingsBroadcastChannel(); -} - -const createSettingsBroadcastChannel = () =>{ - - const name = "odlux_settings"; - const bc: BroadcastChannel = new BroadcastChannel(name); - channels.push({ channel: bc, key: name }); - - bc.onmessage = (eventMessage: MessageEvent) => { - 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)); - } - } - } - } + channels.push({ channel: channel, key: channelName }); +}; -} +export const startBroadcastChannel = (applicationStore: ApplicationStore) => { + store = applicationStore; -const createAuthBroadcastChannel = () => { - const name = "odlux_auth"; - const bc: BroadcastChannel = new BroadcastChannel(name); - channels.push({ channel: bc, key: name }); - - bc.onmessage = (eventMessage: MessageEvent) => { - 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') { + //might decide to use one general broadcast channel with more keys in the future + createAuthBroadcastChannel(); + createSettingsBroadcastChannel(); +}; - if (store?.state.framework.authenticationState.user) { - store?.dispatch(logoutUser()); - store?.dispatch(new ReplaceAction("/login")); - } - } - } -} +const createSettingsBroadcastChannel = () => { -export const getBroadcastChannel = (channelName: string) =>{ - const foundChannel = channels.find(s =>s.key===channelName); - return foundChannel?.channel; -} + const name = "odlux_settings"; + const bc: BroadcastChannel = new BroadcastChannel(name); + channels.push({ channel: bc, key: name }); + bc.onmessage = (eventMessage: MessageEvent) => { + console.log(eventMessage); -export const sendMessage = (data: any, channel: string) =>{ + if (eventMessage.data.key === 'general') { - const foundChannel = channels.find(s =>s.key===channel); - if(foundChannel){ - foundChannel.channel.postMessage(data); - } + 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) => { + 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 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/framework/src/services/index.ts b/sdnr/wt/odlux/framework/src/services/index.ts index 19b451345..85f0708a6 100644 --- a/sdnr/wt/odlux/framework/src/services/index.ts +++ b/sdnr/wt/odlux/framework/src/services/index.ts @@ -18,5 +18,5 @@ export { applicationManager } from './applicationManager'; export { subscribe, unsubscribe } from './notificationService'; export { requestRest } from './restService'; -export { putSettings, getSettings} from './settingsService'; +export { saveUserdata, getUserdata } from './userdataService'; diff --git a/sdnr/wt/odlux/framework/src/services/restService.ts b/sdnr/wt/odlux/framework/src/services/restService.ts index d727e4c9e..a296c52a4 100644 --- a/sdnr/wt/odlux/framework/src/services/restService.ts +++ b/sdnr/wt/odlux/framework/src/services/restService.ts @@ -16,18 +16,13 @@ * ============LICENSE_END========================================================================== */ +import { ReplaceAction } from '../actions/navigationActions'; +import { AddErrorInfoAction } from '../actions/errorActions'; -import { ApplicationStore } from "../store/applicationStore"; -import { ReplaceAction } from "../actions/navigationActions"; -import { AddErrorInfoAction } from "../actions/errorActions"; +import { storeService } from './storeService'; const baseUri = `${ window.location.origin }`; const absUrlPattern = /^https?:\/\//; -let applicationStore: ApplicationStore | null = null; - -export const startRestService = (store: ApplicationStore) => { - applicationStore = store; -}; export const formEncode = (params: { [key: string]: string | number }) => Object.keys(params).map((key) => { return encodeURIComponent(key) + '=' + encodeURIComponent(params[key].toString()); @@ -46,9 +41,9 @@ export const getAccessPolicyByUrl = (url: string) => { DELETE: false, }; - if (!applicationStore) return result; + if (!storeService.applicationStore) return result; - const { state: { framework: { applicationState: { enablePolicy }, authenticationState: { policies }}} } = applicationStore!; + const { state: { framework: { applicationState: { enablePolicy }, authenticationState: { policies } } } } = storeService.applicationStore!; result.GET = true; result.POST = true; @@ -71,7 +66,7 @@ export const getAccessPolicyByUrl = (url: string) => { return result; -} +}; /** Sends a rest request to the given path. * @returns The data, or null it there was any error @@ -87,8 +82,8 @@ export async function requestRest(path: string = '', init: RequestInit = /** 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(path: string = '', init: 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 } = { +export async function requestRestExt(path: string = '', init: 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, }; @@ -100,60 +95,59 @@ export async function requestRestExt(path: string = '', init: RequestInit headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', - ...init.headers - } + ...init.headers, + }, }; - if (!isAbsUrl && authenticate && applicationStore) { - const { state: { framework: { authenticationState: { user } } } } = applicationStore; + 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) { return { ...result, - message: "User is not valid or not logged in." + message: 'User is not valid or not logged in.', }; } (init.headers = { ...init.headers, - 'Authorization': `${user.tokenType} ${user.token}` + 'Authorization': `${user.tokenType} ${user.token}`, //'Authorization': 'Basic YWRtaW46YWRtaW4=' }); } const fetchResult = await fetch(uri, init); - if(fetchResult.status === 403){ - applicationStore && applicationStore.dispatch(new AddErrorInfoAction({title: "Forbidden", message:"Status: [403], access denied."})); + if (fetchResult.status === 403) { + storeService.applicationStore && storeService.applicationStore.dispatch(new AddErrorInfoAction({ title: 'Forbidden', message:'Status: [403], access denied.' })); return { ...result, status: 403, - message: "Forbidden." + message: 'Forbidden.', }; - } - else if (fetchResult.status === 401) { - applicationStore && applicationStore.dispatch(new ReplaceAction(`/login?returnTo=${applicationStore.state.framework.navigationState.pathname}`)); + } else if (fetchResult.status === 401) { + storeService.applicationStore && storeService.applicationStore.dispatch(new ReplaceAction(`/login?returnTo=${storeService.applicationStore.state.framework.navigationState.pathname}`)); return { ...result, status: 401, - message: "Authentication requested by server." + 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")); + 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 + data: data, }; } catch (error) { return { ...result, status: fetchResult.status, message: error && error.message || String(error), - data: undefined + data: undefined, }; } } \ No newline at end of file diff --git a/sdnr/wt/odlux/framework/src/services/settingsService.ts b/sdnr/wt/odlux/framework/src/services/settingsService.ts deleted file mode 100644 index 6633a794d..000000000 --- a/sdnr/wt/odlux/framework/src/services/settingsService.ts +++ /dev/null @@ -1,41 +0,0 @@ -/** - * ============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 function getSettings(partialPath?: string){ - let path = settingsPath; - if(partialPath){ - path+=partialPath - } - - const result = requestRest(path, {method: "GET"}) - return result; - } - - export function putSettings(partialPath: string, data: string){ - - const result = requestRest(settingsPath+partialPath, {method: "PUT", body: data}) - return result; - } - - diff --git a/sdnr/wt/odlux/framework/src/services/storeService.ts b/sdnr/wt/odlux/framework/src/services/storeService.ts new file mode 100644 index 000000000..cbb5987de --- /dev/null +++ b/sdnr/wt/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/framework/src/services/userdataService.ts b/sdnr/wt/odlux/framework/src/services/userdataService.ts new file mode 100644 index 000000000..5c9b576c3 --- /dev/null +++ b/sdnr/wt/odlux/framework/src/services/userdataService.ts @@ -0,0 +1,41 @@ +/** + * ============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 function getUserdata(partialPath?: string){ + let path = settingsPath; + if(partialPath){ + path+=partialPath + } + + const result = requestRest(path, {method: "GET"}) + return result; + } + + export function saveUserdata(partialPath: string, data: string){ + + const result = requestRest(settingsPath+partialPath, {method: "PUT", body: data}) + return result; + } + + diff --git a/sdnr/wt/odlux/framework/src/store/applicationStore.ts b/sdnr/wt/odlux/framework/src/store/applicationStore.ts index a4545eff9..cbe8c20da 100644 --- a/sdnr/wt/odlux/framework/src/store/applicationStore.ts +++ b/sdnr/wt/odlux/framework/src/store/applicationStore.ts @@ -37,7 +37,7 @@ import { updatePolicies } from '../middleware/policies'; export type MiddlewareApi = MiddlewareArg; export interface IFrameworkStoreState { - applicationRegistraion: IApplicationRegistration; + applicationRegistration: IApplicationRegistration; applicationState: IApplicationState; authenticationState: IAuthenticationState; navigationState: INavigationState; @@ -48,7 +48,7 @@ export interface IApplicationStoreState { } const frameworkHandlers = combineActionHandler({ - applicationRegistraion: applicationRegistryHandler, + applicationRegistration: applicationRegistryHandler, applicationState: applicationStateHandler, authenticationState: authenticationStateHandler, navigationState: navigationStateHandler @@ -62,7 +62,7 @@ export const applicationStoreCreator = (): ApplicationStore => { 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 || 0) && middlewares.push(...(reg.middlewares as Middleware[])); + reg && reg.middlewares && Array.isArray(reg.middlewares) && middlewares.push(...(reg.middlewares as Middleware[])); return acc; }, { framework: frameworkHandlers } as any); diff --git a/sdnr/wt/odlux/framework/src/utilities/logLevel.ts b/sdnr/wt/odlux/framework/src/utilities/logLevel.ts new file mode 100644 index 000000000..a198d98a9 --- /dev/null +++ b/sdnr/wt/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/framework/src/views/about.tsx b/sdnr/wt/odlux/framework/src/views/about.tsx index ac219708d..937e74f33 100644 --- a/sdnr/wt/odlux/framework/src/views/about.tsx +++ b/sdnr/wt/odlux/framework/src/views/about.tsx @@ -15,22 +15,18 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +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) => ( `${text}` ); -interface AboutState { - content: string | null; - isCopiedSuccessfully: boolean; - isContentLoadedSucessfully: boolean; -} -type odluxVersion= {version:string,build:string, framework: string, +type OdluxVersion= {version:string,build:string, framework: string, applications:{ configurationApp: string, connectApp: string, @@ -38,25 +34,27 @@ type odluxVersion= {version:string,build:string, framework: string, faultApp: string, helpApp: string, inventoryApp: string, + linkCalculationApp: string, maintenanceApp: string, mediatorApp: string, + networkMapApp: string, permanceHistoryApp: string }}; -type topologyVersion = {version: string, buildTimestamp: string}; - -class AboutComponent extends React.Component { - textarea: React.RefObject; +type TopologyVersion = {version: string, buildTimestamp: string}; +const AboutComponent: FC = (props) => { + + const textareaRef = React.createRef(); + const [content, setContent] = useState(null); + const [isCopiedSuccessfully, setCopySuccess] = useState(false); + const [isContetLoaded, setContentLoaded] = useState(false); - constructor(props: any) { - super(props); - this.state = { content: null, isCopiedSuccessfully:false, isContentLoadedSucessfully: false } - this.textarea = React.createRef(); - this.loadAboutContent(); - } + useEffect(()=>{ + loadAboutContent(); + },[]); - private getMarkOdluxVersionMarkdownTable(data:odluxVersion|null|undefined):string{ + const getMarkOdluxVersionMarkdownTable = (data:OdluxVersion|null|undefined):string => { if(!data) { return ""; }else{ @@ -72,6 +70,8 @@ class AboutComponent extends React.Component { `| InventoryApp | ${data.applications.inventoryApp}|\n `+ `| EventLogApp | ${data.applications.eventLogApp}|\n `+ `| MediatorApp | ${data.applications.mediatorApp}|\n `+ + `| NetworkMapApp | ${data.applications.networkMapApp}|\n `+ + `| LinkCalculatorApp | ${data.applications.linkCalculationApp}|\n `+ `| HelpApp | ${data.applications.helpApp}|\n `; } @@ -80,7 +80,7 @@ class AboutComponent extends React.Component { } } - private getTopologyVersionMarkdownTable(data: topologyVersion|null|undefined){ + const getTopologyVersionMarkdownTable = (data: TopologyVersion|null|undefined) => { if(!data){ return "No version"; } @@ -92,7 +92,7 @@ class AboutComponent extends React.Component { } } - private loadAboutContent(): void { + const loadAboutContent = (): void => { const baseUri = window.location.pathname.substring(0,window.location.pathname.lastIndexOf("/")+1); const init = { 'method': 'GET', @@ -102,7 +102,7 @@ class AboutComponent extends React.Component { } }; const p1 = requestRestExt('/about',init); - const p2 = requestRestExt(`${baseUri}version.json`); + const p2 = requestRestExt(`${baseUri}version.json`); const p3 = requestRestExt(`/topology/info/version`); Promise.all([p1,p2, p3]).then((responses) => { @@ -110,31 +110,30 @@ class AboutComponent extends React.Component { 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 ? this.getMarkOdluxVersionMarkdownTable(response2.data) : `${response2.message}` || "ODLUX Server error"); - const content3 = `\n## Topology API Version Info\n`+(response3.status == 200 ? this.getTopologyVersionMarkdownTable(response3.data): `Topology API not available`); + 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; - this.setState({ content: (content + content2 + content3 ) || null, isContentLoadedSucessfully: loadedSucessfully }); + setContent((content + content2 + content3 ) || null); + setContentLoaded(loadedSucessfully); }).catch((error) => { - this.setState({ content: error }) - }) + setContent(error); + }); } - copyToClipboard = (e: React.MouseEvent) =>{ + const copyToClipboard = (e: React.MouseEvent) =>{ e.preventDefault(); - if(this.textarea.current!==null){ - this.textarea.current.select(); + 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(); } - this.setState({isCopiedSuccessfully: true}); - window.setTimeout(()=>{this.setState({isCopiedSuccessfully: false});},2000); + setCopySuccess(true); + window.setTimeout(()=>{ setCopySuccess(false);},2000); } } - render() { - const markedOptions: marked.MarkedOptions = { gfm: true, breaks: false, @@ -157,17 +156,17 @@ class AboutComponent extends React.Component { const style: React.CSSProperties = {}; const containerStyle = { overflow: "auto", paddingRight: "20px" } - const html = (marked(this.state.content || 'loading', { renderer: markedOptions && markedOptions.renderer || defaultRenderer })); + const html = (marked(content || 'loading', { renderer: markedOptions && markedOptions.renderer || defaultRenderer })); return (
- { this.state.isContentLoadedSucessfully && + { isContetLoaded &&
- { - this.state.isCopiedSuccessfully && + isCopiedSuccessfully && copied successfully @@ -183,13 +182,12 @@ class AboutComponent extends React.Component {