aboutsummaryrefslogtreecommitdiffstats
path: root/sdnr/wt/odlux/framework/src/views
diff options
context:
space:
mode:
Diffstat (limited to 'sdnr/wt/odlux/framework/src/views')
-rw-r--r--sdnr/wt/odlux/framework/src/views/about.tsx78
-rw-r--r--sdnr/wt/odlux/framework/src/views/frame.tsx143
-rw-r--r--sdnr/wt/odlux/framework/src/views/home.tsx33
-rw-r--r--sdnr/wt/odlux/framework/src/views/login.tsx320
-rw-r--r--sdnr/wt/odlux/framework/src/views/settings.tsx45
5 files changed, 274 insertions, 345 deletions
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) => (
`<a target="_blank" rel="noopener noreferrer" href="${href}" title="${title}">${text}</a>`
);
-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<any, AboutState> {
- textarea: React.RefObject<HTMLTextAreaElement>;
+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);
- 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<any, AboutState> {
`| 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<any, AboutState> {
}
}
- 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<any, AboutState> {
}
}
- 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<any, AboutState> {
}
};
const p1 = requestRestExt<string>('/about',init);
- const p2 = requestRestExt<odluxVersion>(`${baseUri}version.json`);
+ const p2 = requestRestExt<OdluxVersion>(`${baseUri}version.json`);
const p3 = requestRestExt<any>(`/topology/info/version`);
Promise.all([p1,p2, p3]).then((responses) => {
@@ -110,31 +110,30 @@ class AboutComponent extends React.Component<any, AboutState> {
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<HTMLButtonElement>) =>{
+ const copyToClipboard = (e: React.MouseEvent<HTMLButtonElement>) =>{
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<any, AboutState> {
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 (
<div style={containerStyle}>
- { this.state.isContentLoadedSucessfully &&
+ { isContetLoaded &&
<div style={{float: "right", marginRight: "10px"}}>
- <Button aria-label="copy-version-information-button" color="inherit" variant="contained" onClick={e => this.copyToClipboard(e)}>
+ <Button aria-label="copy-version-information-button" color="inherit" variant="contained" onClick={e => copyToClipboard(e)}>
Copy to clipboard
</Button>
{
- this.state.isCopiedSuccessfully &&
+ isCopiedSuccessfully &&
<Typography variant="body1" style={{color: "green"}} align="center">
copied successfully
</Typography>
@@ -183,13 +182,12 @@ class AboutComponent extends React.Component<any, AboutState> {
<form>
<textarea
style={{opacity: ".01"}}
- ref={this.textarea}
- value={this.state.content || ''}
+ ref={textareaRef}
+ value={content || ''}
/>
</form>
</div>
);
- }
};
export const About = AboutComponent;
diff --git a/sdnr/wt/odlux/framework/src/views/frame.tsx b/sdnr/wt/odlux/framework/src/views/frame.tsx
index 4676f5ac2..4a93cf0ae 100644
--- a/sdnr/wt/odlux/framework/src/views/frame.tsx
+++ b/sdnr/wt/odlux/framework/src/views/frame.tsx
@@ -15,15 +15,11 @@
* the License.
* ============LICENSE_END==========================================================================
*/
-import * as React from 'react';
+import React, { FC, memo } from 'react';
import { HashRouter as Router, Route, Redirect, Switch } 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 { faHome, faAddressBook, faSignInAlt, faCog } from '@fortawesome/free-solid-svg-icons'
-
+import { makeStyles } from '@mui/styles';
import { SnackbarProvider } from 'notistack';
import { ConfirmProvider } from 'material-ui-confirm';
@@ -42,8 +38,14 @@ import UserSettings from '../views/settings';
import applicationService from '../services/applicationManager';
+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) => {
-const styles = (theme: Theme) => createStyles({
+ return {
root: {
flexGrow: 1,
height: '100%',
@@ -61,74 +63,69 @@ const styles = (theme: Theme) => createStyles({
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>
+ <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>
+ );
+});
-type FrameProps = WithStyles<typeof styles>;
-
-class FrameComponent extends React.Component<FrameProps>{
-
- render() {
- const registrations = applicationService.applications;
- const { classes } = this.props;
- return (
- <ConfirmProvider>
- <SnackbarProvider maxSnack={3}>
- <Router>
- <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={faHome} >
- <Home />
- </AppFrame>
- )} />
- <Route path="/about" component={() => (
- <AppFrame title={"About"} icon={faAddressBook} >
- <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 />
- </AppFrame>
- )} /> : null}
- <Route path="/login" component={() => (
- <AppFrame title={"Login"} icon={faSignInAlt} >
- <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 = withStyles(styles)(FrameComponent);
+export const Frame = FrameComponent;
export default Frame;
diff --git a/sdnr/wt/odlux/framework/src/views/home.tsx b/sdnr/wt/odlux/framework/src/views/home.tsx
index 92fd0b262..72c5059e1 100644
--- a/sdnr/wt/odlux/framework/src/views/home.tsx
+++ b/sdnr/wt/odlux/framework/src/views/home.tsx
@@ -16,50 +16,39 @@
* ============LICENSE_END==========================================================================
*/
-import * as React from 'react';
-import { IApplicationStoreState } from "../store/applicationStore";
-import connect, { Connect, IDispatcher } from "../flux/connect";
+import React, {FC, useState} from 'react';
import applicationService from '../services/applicationManager';
-type props = Connect<typeof mapProps, typeof mapDispatch>;
-type SettingsEntry = { name: string, element: JSX.Element }
+type DashboardElement = { name: string, element: JSX.Element };
-
-const mapProps = (state: IApplicationStoreState) => ({
-});
-
-const mapDispatch = (dispatcher: IDispatcher) => ({
-});
-
-const DashboardView: React.FunctionComponent<props> = (props) => {
+const DashboardView: FC = (props) => {
const registrations = applicationService.applications;
- const [selectedIndex] = React.useState(0);
+ const [selectedIndex] = useState(0);
- let settingsArray: SettingsEntry[] = [];
+ let dashboardArray: DashboardElement[] = [];
- let settingsElements: (SettingsEntry)[] = Object.keys(registrations).map(p => {
+ let dashboardElements: (DashboardElement)[] = Object.keys(registrations).map(p => {
const application = registrations[p];
if (application.dashbaordElement) {
- const value: SettingsEntry = { name: application.menuEntry?.toString()!, element: <application.dashbaordElement /> };
+ const value: DashboardElement = { name: application.menuEntry?.toString()!, element: <application.dashbaordElement /> };
return value;
} else {
return null;
}
- }).filter((x): x is SettingsEntry => x !== null);
-
+ }).filter((x): x is DashboardElement => x !== null);
- settingsArray.push(...settingsElements);
+ dashboardArray.push(...dashboardElements);
return <div>
<div>
<div>
{
- settingsArray[selectedIndex]?.element
+ dashboardArray[selectedIndex]?.element
}
</div>
</div>
@@ -67,4 +56,4 @@ const DashboardView: React.FunctionComponent<props> = (props) => {
}
-export default connect(mapProps, mapDispatch)(DashboardView);
+export default DashboardView; \ 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 e037edf82..46c0872bc 100644
--- a/sdnr/wt/odlux/framework/src/views/login.tsx
+++ b/sdnr/wt/odlux/framework/src/views/login.tsx
@@ -15,39 +15,34 @@
* the License.
* ============LICENSE_END==========================================================================
*/
-import * as React from 'react';
-import { withRouter, RouteComponentProps } from 'react-router-dom';
+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 FormControlLabel from '@mui/material/FormControlLabel';
-import Checkbox from '@mui/material/Checkbox';
import Input from '@mui/material/Input';
import InputLabel from '@mui/material/InputLabel';
-import LockIcon from '@mui/icons-material/LockOutlined';
import Paper from '@mui/material/Paper';
-import Typography from '@mui/material/Typography';
import { Theme } from '@mui/material/styles';
+import Typography from '@mui/material/Typography';
-import { WithStyles } from '@mui/styles';
-import withStyles from '@mui/styles/withStyles';
-import createStyles from '@mui/styles/createStyles';
+import { makeStyles } from '@mui/styles';
-import connect, { Connect, IDispatcher } from '../flux/connect';
+import { useApplicationDispatch, useSelectApplicationState } from '../flux/connect';
import authenticationService from '../services/authenticationService';
-import { updateExternalLoginProviderAsyncActionCreator } from '../actions/loginProvider';
import { loginUserAction, UpdatePolicies } from '../actions/authentication';
+import { updateExternalLoginProviderAsyncActionCreator } from '../actions/loginProvider';
-import { IApplicationStoreState } from '../store/applicationStore';
import { AuthPolicy, AuthToken, User } from '../models/authentication';
-import Menu from '@mui/material/Menu';
-import { MenuItem } from '@mui/material';
-const styles = (theme: Theme) => createStyles({
+const loginIcon = require('../assets/icons/User.svg');
+
+const styles = makeStyles((theme: Theme) =>{
+ return{
layout: {
width: 'auto',
display: 'block', // Fix IE11 issue.
@@ -91,204 +86,165 @@ const styles = (theme: Theme) => createStyles({
padding: '0 10px',
color: 'grey'
}
+};
});
-const mapProps = (state: IApplicationStoreState) => ({
- search: state.framework.navigationState.search,
- authentication: state.framework.applicationState.authentication,
- externalLoginProviders: state.framework.applicationState.externalLoginProviders ,
-});
-
-const mapDispatch = (dispatcher: IDispatcher) => ({
- updateExternalProviders: () => dispatcher.dispatch(updateExternalLoginProviderAsyncActionCreator()),
- updateAuthentication: (token: AuthToken | null) => {
- const user = token && new User(token) || undefined;
- dispatcher.dispatch(loginUserAction(user));
- },
- updatePolicies: (policies?: AuthPolicy[]) => {
- return dispatcher.dispatch(new UpdatePolicies(policies));
- },
-});
-
-type LoginProps = RouteComponentProps<{}> & WithStyles<typeof styles> & Connect<typeof mapProps, typeof mapDispatch>;
-
-interface ILoginState {
- externalProviderAnchor: HTMLElement | null;
- busy: boolean;
- username: string;
- password: string;
- scope: string;
- message: string;
- isServerReady: boolean;
- providers: {
- id: string;
- title: string;
- loginUrl: string;
- }[] | null;
-}
+type LoginProps = RouteComponentProps;
// todo: ggf. redirect to einbauen
-class LoginComponent extends React.Component<LoginProps, ILoginState> {
+const LoginComponent: FC<LoginProps> = (props) => {
- constructor(props: LoginProps) {
- super(props);
-
- this.state = {
- externalProviderAnchor: null,
- busy: false,
- username: '',
- password: '',
- scope: 'sdn',
- message: '',
- providers: null,
- isServerReady: false
- };
+ 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));
}
- async componentDidMount(){
- if (this.props.authentication === "oauth" && (this.props.externalLoginProviders == null || this.props.externalLoginProviders.length === 0)){
- this.props.updateExternalProviders();
+ 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 =>{
- this.setState({isServerReady: result});
+ setIsServerReady(result);
})
+ },[]);
-
-
- }
-
- private setExternalProviderAnchor = (el: HTMLElement | null) => {
- this.setState({externalProviderAnchor: el })
- }
-
- render(): JSX.Element {
- const { classes } = this.props;
- const areProvidersAvailable = this.props.externalLoginProviders && this.props.externalLoginProviders.length > 0;
- return (
- <>
- <CssBaseline />
- <main className={classes.layout}>
- <Paper className={classes.paper}>
- <Avatar className={classes.avatar}>
- <LockIcon />
- </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="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={this.state.busy}
- value={this.state.username}
- onChange={event => { this.setState({ username: 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={this.state.busy}
- value={this.state.password}
- onChange={event => { this.setState({ password: event.target.value }) }}
- />
- </FormControl>
- <FormControl variant="standard" margin="normal" required fullWidth>
- <InputLabel htmlFor="password">Domain</InputLabel>
- <Input
- name="scope"
- type="scope"
- id="scope"
- disabled={this.state.busy}
- value={this.state.scope}
- onChange={event => { this.setState({ scope: event.target.value }) }}
- />
- </FormControl>
- <Button
- aria-label="login-button"
- type="submit"
- fullWidth
- variant="contained"
- color="inherit"
- disabled={this.state.busy}
- className={classes.submit}
- onClick={this.onSignIn}
- >
- Sign in
- </Button>
-
- </form>
- {this.state.message && <Alert severity="error">{this.state.message}</Alert>}
- </Paper>
- </main>
- </>
- );
- }
-
- private onSignIn = async (event: React.MouseEvent<HTMLButtonElement>) => {
+ const onSignIn = async (event: React.MouseEvent<HTMLButtonElement>) => {
event.preventDefault();
+
+ setBusy(true);
- this.setState({ busy: true });
-
- const token = this.props.authentication === "oauth"
- ? await authenticationService.authenticateUserOAuth(this.state.username, this.state.password, this.state.scope)
- : await authenticationService.authenticateUserBasicAuth(this.state.username, this.state.password, this.state.scope);
+ const token = authentication === "oauth"
+ ? await authenticationService.authenticateUserOAuth(username, password, scope)
+ : await authenticationService.authenticateUserBasicAuth(username, password, scope);
- this.props.updateAuthentication(token);
- this.setState({ busy: false });
+ updateAuthentication(token);
+ setBusy(false);
if (token) {
- const query = this.props.search && this.props.search.replace(/^\?/, "").split('&').map(e => e.split("="));
+ const query = search && search.replace(/^\?/, "").split('&').map(e => e.split("="));
const returnTo = query && query.find(e => e[0] === "returnTo");
- this.props.history.replace(returnTo && returnTo[1] || "/");
+ props.history.replace(returnTo && returnTo[1] || "/");
}
else {
- if(!this.state.isServerReady){
+ if(!isServerReady){
const ready = await authenticationService.getServerReadyState();
if(ready){
- this.setState({isServerReady: true});
+ setIsServerReady(true);
}else{
- this.setState({message: "Login is currently not possible. Please re-try in a few minutes. If the problem persits, ask your administrator for assistence."});
+ setMessage("Login is currently not possible. Please re-try in a few minutes. If the problem persists, ask your administrator for assistance.");
}
}else{
- this.setState({
- message: "Could not log in. Please check your credentials or ask your administrator for assistence.",
- password: ""
- })
+ 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 = withStyles(styles)(withRouter(connect(mapProps, mapDispatch)(LoginComponent)));
+export const Login = withRouter(LoginComponent);
export default Login; \ No newline at end of file
diff --git a/sdnr/wt/odlux/framework/src/views/settings.tsx b/sdnr/wt/odlux/framework/src/views/settings.tsx
index a6b940bfa..5973db9a0 100644
--- a/sdnr/wt/odlux/framework/src/views/settings.tsx
+++ b/sdnr/wt/odlux/framework/src/views/settings.tsx
@@ -16,30 +16,18 @@
* ============LICENSE_END==========================================================================
*/
-import * as React from 'react';
-import { IApplicationStoreState } from "../store/applicationStore";
-import connect, { Connect, IDispatcher } from "../flux/connect";
+import React, {FC, useState } from 'react';
+import { useApplicationDispatch } from "../flux/connect";
-import applicationService from '../services/applicationManager';
-import { makeStyles } from '@mui/styles';
import { Divider, List, ListItem, ListItemText, Paper } from '@mui/material';
+import { makeStyles } from '@mui/styles';
+import applicationService from '../services/applicationManager';
-import { GeneralUserSettings } from '../components/settings/general'
import { GoBackAction } from '../actions/navigationActions';
+import { GeneralUserSettings } from '../components/settings/general';
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())
-});
+type SettingsEntry = { name: string, element: JSX.Element };
const styles = makeStyles({
sectionMargin: {
@@ -47,7 +35,6 @@ const styles = makeStyles({
marginBottom: "15px"
},
elementMargin: {
-
marginLeft: "10px"
},
menu: {
@@ -55,15 +42,17 @@ const styles = makeStyles({
}
});
-const UserSettings: React.FunctionComponent<props> = (props) => {
+const UserSettings: FC = (props) => {
- const classes = styles();
- const registrations = applicationService.applications;
+ const dispatch = useApplicationDispatch();
+ const goBack = () => dispatch(new GoBackAction());
+
+ const [selectedIndex, setSelectedIndex] = useState(0);
- const [selectedIndex, setSelectedIndex] = React.useState(0);
+ const registrations = applicationService.applications;
const navigateBack = () => {
- props.goBack();
+ goBack();
}
let settingsArray: SettingsEntry[] = [];
@@ -71,7 +60,6 @@ const UserSettings: React.FunctionComponent<props> = (props) => {
//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];
@@ -93,6 +81,8 @@ const UserSettings: React.FunctionComponent<props> = (props) => {
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%" }}>
@@ -101,7 +91,7 @@ const UserSettings: React.FunctionComponent<props> = (props) => {
settingsArray.map((el, index) => {
return (
<>
- <ListItem selected={selectedIndex === index} button onClick={e => { onSelectElement(e, index) }} aria-label={toAriaLabel(el?.name+"-settings")}>
+ <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 />
@@ -110,7 +100,6 @@ const UserSettings: React.FunctionComponent<props> = (props) => {
}
</List>
</Paper>
-
</div>
<div style={{ height: "100%", width: "80%", marginLeft: 15 }}>
<div style={{ height: "100%" }}>
@@ -123,4 +112,4 @@ const UserSettings: React.FunctionComponent<props> = (props) => {
}
-export default connect(mapProps, mapDispatch)(UserSettings);
+export default UserSettings; \ No newline at end of file