diff options
Diffstat (limited to 'sdnr/wt/odlux/framework/src/views')
-rw-r--r-- | sdnr/wt/odlux/framework/src/views/frame.tsx | 10 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/src/views/home.tsx | 185 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/src/views/login.tsx | 79 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/src/views/settings.tsx | 126 |
4 files changed, 329 insertions, 71 deletions
diff --git a/sdnr/wt/odlux/framework/src/views/frame.tsx b/sdnr/wt/odlux/framework/src/views/frame.tsx index b4cc43e0b..1c78dd297 100644 --- a/sdnr/wt/odlux/framework/src/views/frame.tsx +++ b/sdnr/wt/odlux/framework/src/views/frame.tsx @@ -19,7 +19,7 @@ import * as React from 'react'; import { HashRouter as Router, Route, Redirect, Switch } from 'react-router-dom';
import { withStyles, WithStyles, createStyles, Theme } from '@material-ui/core/styles';
-import { faHome, faAddressBook, faSignInAlt } from '@fortawesome/free-solid-svg-icons';
+import { faHome, faAddressBook, faSignInAlt, faCog } from '@fortawesome/free-solid-svg-icons';
import { SnackbarProvider } from 'notistack';
import { ConfirmProvider } from 'material-ui-confirm';
@@ -34,6 +34,7 @@ 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';
@@ -58,6 +59,8 @@ const styles = (theme: Theme) => createStyles({ toolbar: theme.mixins.toolbar as any
});
+
+
type FrameProps = WithStyles<typeof styles>;
class FrameComponent extends React.Component<FrameProps>{
@@ -89,6 +92,11 @@ class FrameComponent extends React.Component<FrameProps>{ <About />
</AppFrame>
)} />
+ <Route path="/settings" component={() => (
+ <AppFrame title={"Settings"} icon={faCog} >
+ <UserSettings />
+ </AppFrame>
+ )} />
{process.env.NODE_ENV === "development" ? <Route path="/test" component={() => (
<AppFrame title={"Test"} icon={faAddressBook} >
<Test />
diff --git a/sdnr/wt/odlux/framework/src/views/home.tsx b/sdnr/wt/odlux/framework/src/views/home.tsx index 0e1d487e3..176de02ab 100644 --- a/sdnr/wt/odlux/framework/src/views/home.tsx +++ b/sdnr/wt/odlux/framework/src/views/home.tsx @@ -34,6 +34,16 @@ const styles = (theme: Theme) => createStyles({ const scrollbar = { overflow: "auto", paddingRight: "20px" } +let connectionStatusinitialLoad = true; +let connectionStatusinitialStateChanged = false; +let connectionStatusDataLoad: number[] = [0, 0, 0, 0]; +let connectionTotalCount = 0; + +let alarmStatusinitialLoad = true; +let alarmStatusinitialStateChanged = false; +let alarmStatusDataLoad: number[] = [0, 0, 0, 0]; +let alarmTotalCount = 0; + const mapProps = (state: IApplicationStoreState) => ({ connectionStatusCount: state.connect.connectionStatusCount, alarmStatus: state.fault.faultStatus @@ -55,16 +65,34 @@ class Home extends React.Component<HomeComponentProps> { render(): JSX.Element { const { classes } = this.props; + if (!this.props.connectionStatusCount.isLoadingConnectionStatusChart) { + connectionStatusDataLoad = [ + this.props.connectionStatusCount.Connected, + this.props.connectionStatusCount.Connecting, + this.props.connectionStatusCount.Disconnected, + this.props.connectionStatusCount.UnableToConnect + ]; + connectionTotalCount = this.props.connectionStatusCount.Connected + this.props.connectionStatusCount.Connecting + + this.props.connectionStatusCount.Disconnected + this.props.connectionStatusCount.UnableToConnect; + + } + + if (!this.props.alarmStatus.isLoadingAlarmStatusChart) { + alarmStatusDataLoad = [ + this.props.alarmStatus.critical, + this.props.alarmStatus.major, + this.props.alarmStatus.minor, + this.props.alarmStatus.warning + ]; + alarmTotalCount = this.props.alarmStatus.critical + this.props.alarmStatus.major + + this.props.alarmStatus.minor + this.props.alarmStatus.warning; + } + /** Available Network Connection Status chart data */ const connectionStatusData = { labels: ['Connected', 'Connecting', 'Disconnected', 'UnableToConnect'], datasets: [{ - data: [ - this.props.connectionStatusCount.Connected, - this.props.connectionStatusCount.Connecting, - this.props.connectionStatusCount.Disconnected, - this.props.connectionStatusCount.UnableToConnect - ], + data: connectionStatusDataLoad, backgroundColor: [ 'rgb(0, 153, 51)', 'rgb(255, 102, 0)', @@ -86,6 +114,28 @@ class Home extends React.Component<HomeComponentProps> { }] }; + /** Loading Connection Status chart */ + const connectionStatusisLoading = { + labels: ['Loading chart...'], + datasets: [{ + data: [1], + backgroundColor: [ + 'rgb(255, 255, 255)' + ] + }] + }; + + /** Loading Alarm Status chart */ + const alarmStatusisLoading = { + labels: ['Loading chart...'], + datasets: [{ + data: [1], + backgroundColor: [ + 'rgb(255, 255, 255)' + ] + }] + }; + /** Connection status options */ let labels: String[] = ['Connected', 'Connecting', 'Disconnected', 'UnableToConnect']; const connectionStatusOptions = { @@ -153,12 +203,7 @@ class Home extends React.Component<HomeComponentProps> { 'Warning' ], datasets: [{ - data: [ - this.props.alarmStatus.critical, - this.props.alarmStatus.major, - this.props.alarmStatus.minor, - this.props.alarmStatus.warning - ], + data: alarmStatusDataLoad, backgroundColor: [ 'rgb(240, 25, 10)', 'rgb(240, 133, 10)', @@ -241,17 +286,25 @@ class Home extends React.Component<HomeComponentProps> { <div style={scrollbar} > <h1>Welcome to ODLUX</h1> <div className={classes.pageWidthSettings}> - {this.checkConnectionStatus() ? - <Doughnut - data={connectionStatusData} - type={Doughnut} - width={500} - height={500} - options={connectionStatusOptions} - plugins={connectionStatusPlugins} - /> + {this.checkElementsAreLoaded() ? + this.checkConnectionStatus() && connectionTotalCount != 0 ? + <Doughnut + data={connectionStatusData} + type={Doughnut} + width={500} + height={500} + options={connectionStatusOptions} + plugins={connectionStatusPlugins} + /> + : <Doughnut + data={connectionStatusUnavailableData} + type={Doughnut} + width={500} + height={500} + options={connectionStatusUnavailableOptions} + plugins={connectionStatusPlugins} /> : <Doughnut - data={connectionStatusUnavailableData} + data={connectionStatusisLoading} type={Doughnut} width={500} height={500} @@ -261,17 +314,26 @@ class Home extends React.Component<HomeComponentProps> { } </div> <div className={classes.pageWidthSettings}> - {this.checkAlarmStatus() ? - <Doughnut - data={alarmStatusData} - type={Doughnut} - width={500} - height={500} - options={alarmStatusOptions} - plugins={alarmStatusPlugins} - /> + {this.checkAlarmsAreLoaded() ? + this.checkAlarmStatus() && alarmTotalCount != 0 ? + <Doughnut + data={alarmStatusData} + type={Doughnut} + width={500} + height={500} + options={alarmStatusOptions} + plugins={alarmStatusPlugins} + /> + : <Doughnut + data={alarmStatusUnavailableData} + type={Doughnut} + width={500} + height={500} + options={alarmStatusUnavailableOptions} + plugins={alarmStatusPlugins} + /> : <Doughnut - data={alarmStatusUnavailableData} + data={alarmStatusisLoading} type={Doughnut} width={500} height={500} @@ -288,23 +350,74 @@ class Home extends React.Component<HomeComponentProps> { /** Check if connection status data available */ public checkConnectionStatus = () => { let statusCount = this.props.connectionStatusCount; - if (statusCount.Connected == 0 && statusCount.Connecting == 0 && statusCount.Disconnected == 0 && statusCount.UnableToConnect == 0) { - return false; + if (statusCount.isLoadingConnectionStatusChart) { + return true; } - else + if (statusCount.Connected == 0 && statusCount.Connecting == 0 && statusCount.Disconnected == 0 + && statusCount.UnableToConnect == 0) { + return false; + } else { return true; + } + } + + /** Check if connection status chart data is loaded */ + public checkElementsAreLoaded = () => { + let isLoadingCheck = this.props.connectionStatusCount; + if (connectionStatusinitialLoad && !isLoadingCheck.isLoadingConnectionStatusChart) { + if (this.checkConnectionStatus()) { + connectionStatusinitialLoad = false; + return true; + } + return false; + } else if (connectionStatusinitialLoad && isLoadingCheck.isLoadingConnectionStatusChart) { + connectionStatusinitialLoad = false; + connectionStatusinitialStateChanged = true; + return !isLoadingCheck.isLoadingConnectionStatusChart; + } else if (connectionStatusinitialStateChanged) { + if (!isLoadingCheck.isLoadingConnectionStatusChart) { + connectionStatusinitialStateChanged = false; + } + return !isLoadingCheck.isLoadingConnectionStatusChart; + } + return true; } /** Check if alarms data available */ public checkAlarmStatus = () => { let alarmCount = this.props.alarmStatus; + if (alarmCount.isLoadingAlarmStatusChart) { + return true; + } if (alarmCount.critical == 0 && alarmCount.major == 0 && alarmCount.minor == 0 && alarmCount.warning == 0) { return false; } - else + else { return true; + } } + /** Check if alarm status chart data is loaded */ + public checkAlarmsAreLoaded = () => { + let isLoadingCheck = this.props.alarmStatus; + if (alarmStatusinitialLoad && !isLoadingCheck.isLoadingAlarmStatusChart) { + if (this.checkAlarmStatus()) { + alarmStatusinitialLoad = false; + return true; + } + return false; + } else if (alarmStatusinitialLoad && isLoadingCheck.isLoadingAlarmStatusChart) { + alarmStatusinitialLoad = false; + alarmStatusinitialStateChanged = true; + return !isLoadingCheck.isLoadingAlarmStatusChart; + } else if (alarmStatusinitialStateChanged) { + if (!isLoadingCheck.isLoadingAlarmStatusChart) { + alarmStatusinitialStateChanged = false; + } + return !isLoadingCheck.isLoadingAlarmStatusChart; + } + return true; + } } export default withStyles(styles)(withRouter(connect(mapProps, mapDispatch)(Home)));
\ No newline at end of file diff --git a/sdnr/wt/odlux/framework/src/views/login.tsx b/sdnr/wt/odlux/framework/src/views/login.tsx index be1fb801f..53219facd 100644 --- a/sdnr/wt/odlux/framework/src/views/login.tsx +++ b/sdnr/wt/odlux/framework/src/views/login.tsx @@ -36,7 +36,7 @@ import connect, { Connect, IDispatcher } from '../flux/connect'; import authenticationService from '../services/authenticationService'; import { updateExternalLoginProviderAsyncActionCreator } from '../actions/loginProvider'; -import { UpdatePolicies, UpdateUser } from '../actions/authentication'; +import { loginUserAction, UpdatePolicies } from '../actions/authentication'; import { IApplicationStoreState } from '../store/applicationStore'; import { AuthPolicy, AuthToken, User } from '../models/authentication'; @@ -73,6 +73,20 @@ const styles = (theme: Theme) => createStyles({ 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' + } }); const mapProps = (state: IApplicationStoreState) => ({ @@ -85,7 +99,7 @@ const mapDispatch = (dispatcher: IDispatcher) => ({ updateExternalProviders: () => dispatcher.dispatch(updateExternalLoginProviderAsyncActionCreator()), updateAuthentication: (token: AuthToken | null) => { const user = token && new User(token) || undefined; - dispatcher.dispatch(new UpdateUser(user)); + dispatcher.dispatch(loginUserAction(user)); }, updatePolicies: (policies?: AuthPolicy[]) => { return dispatcher.dispatch(new UpdatePolicies(policies)); @@ -138,6 +152,7 @@ class LoginComponent extends React.Component<LoginProps, ILoginState> { render(): JSX.Element { const { classes } = this.props; + const areProvidersAvailable = this.props.externalLoginProviders && this.props.externalLoginProviders.length > 0; return ( <> <CssBaseline /> @@ -148,6 +163,32 @@ class LoginComponent extends React.Component<LoginProps, ILoginState> { </Avatar> <Typography variant="caption">Sign in</Typography> <form className={classes.form}> + + + {areProvidersAvailable && + <> + { + this.props.externalLoginProviders!.map((provider, index) => ( + <Button + aria-controls="externalLogin" + aria-label={"external-login-identity-provider-" + (index + 1)} + aria-haspopup="true" + fullWidth + variant="contained" + color="primary" + 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 margin="normal" required fullWidth> <InputLabel htmlFor="username">Username</InputLabel> <Input id="username" name="username" autoComplete="username" autoFocus @@ -178,10 +219,6 @@ class LoginComponent extends React.Component<LoginProps, ILoginState> { onChange={event => { this.setState({ scope: event.target.value }) }} /> </FormControl> - <FormControlLabel - control={<Checkbox value="remember" color="secondary" />} - label="Remember me" - /> <Button aria-label="login-button" type="submit" @@ -193,34 +230,8 @@ class LoginComponent extends React.Component<LoginProps, ILoginState> { onClick={this.onSignIn} > Sign in - </Button> - { this.props.externalLoginProviders && this.props.externalLoginProviders.length > 0 - ? - [ - <Button - aria-controls="externalLogin" - aria-haspopup="true" - fullWidth - variant="contained" - color="primary" - className={classes.submit} onClick={(ev) => { this.setExternalProviderAnchor(ev.currentTarget); }}> - Use external Login - </Button>, - <Menu - anchorEl={this.state.externalProviderAnchor} - keepMounted - open={Boolean(this.state.externalProviderAnchor)} - onClose={() => { this.setExternalProviderAnchor(null); }} - > - { - this.props.externalLoginProviders.map((provider) => ( - <MenuItem key={provider.id} onClick={() => { window.location = provider.loginUrl as any; } }>{ provider.title} </MenuItem> - )) - } - </Menu> - ] - : null - } + </Button> + </form> {this.state.message && <Alert severity="error">{this.state.message}</Alert>} </Paper> diff --git a/sdnr/wt/odlux/framework/src/views/settings.tsx b/sdnr/wt/odlux/framework/src/views/settings.tsx new file mode 100644 index 000000000..f1a8ab35a --- /dev/null +++ b/sdnr/wt/odlux/framework/src/views/settings.tsx @@ -0,0 +1,126 @@ +/** + * ============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 * as React from 'react'; +import { IApplicationStoreState } from "../store/applicationStore"; +import connect, { Connect, IDispatcher } from "../flux/connect"; + +import applicationService from '../services/applicationManager'; +import { makeStyles } from '@material-ui/styles'; +import { Divider, List, ListItem, ListItemText, Paper } from '@material-ui/core'; + +import { GeneralUserSettings } from '../components/settings/general' +import { GoBackAction } from '../actions/navigationActions'; +import { toAriaLabel } from '../utilities/yangHelper'; + +type props = Connect<typeof mapProps, typeof mapDispatch>; + +type SettingsEntry = { name: string, element: JSX.Element } + + +const mapProps = (state: IApplicationStoreState) => ({ + +}); + +const mapDispatch = (dispatcher: IDispatcher) => ({ + goBack: () => dispatcher.dispatch(new GoBackAction()) +}); + +const styles = makeStyles({ + sectionMargin: { + marginTop: "30px", + marginBottom: "15px" + }, + elementMargin: { + + marginLeft: "10px" + }, + menu: { + flex: "1 0 0%", + } +}); + +const UserSettings: React.FunctionComponent<props> = (props) => { + + const classes = styles(); + const registrations = applicationService.applications; + + const [selectedIndex, setSelectedIndex] = React.useState(0); + + const navigateBack = () => { + props.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); + } + + 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 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 connect(mapProps, mapDispatch)(UserSettings); |