Create wt-odlux directory
[ccsdk/features.git] / sdnr / wt / odlux / framework / src / views / login.tsx
index 8eb7a6c..46c0872 100644 (file)
  * 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,184 +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;
-  providers: {
-    id: string;
-    title: string;
-    loginUrl: string;
-  }[] | null;
-}
 
+type LoginProps = RouteComponentProps;
 
 // todo: ggf. redirect to einbauen
-class LoginComponent extends React.Component<LoginProps, ILoginState> {
-
-  constructor(props: LoginProps) {
-    super(props);
-
-    this.state = {
-      externalProviderAnchor: null,
-      busy: false,
-      username: '',
-      password: '',
-      scope: 'sdn',
-      message: '',
-      providers: null,
-    };
-  }
-
-  async componentDidMount(){
-     if (this.props.authentication === "oauth" && (this.props.externalLoginProviders == null || this.props.externalLoginProviders.length === 0)){
-       this.props.updateExternalProviders();
-     }
+const LoginComponent:  FC<LoginProps> = (props) => {
+
+  const search = useSelectApplicationState(state => state.framework.navigationState.search);
+  const authentication = useSelectApplicationState(state => state.framework.applicationState.authentication);
+  const externalLoginProviders = useSelectApplicationState(state => state.framework.applicationState.externalLoginProviders);
+  
+  const dispatch = useApplicationDispatch();
+  const updateExternalProviders = () => dispatch(updateExternalLoginProviderAsyncActionCreator());
+  const updateAuthentication = (token: AuthToken | null) => {
+    const user = token && new User(token) || undefined;
+    dispatch(loginUserAction(user));
   }
-
-  private setExternalProviderAnchor = (el: HTMLElement | null) => {
-    this.setState({externalProviderAnchor: el })
+  const updatePolicies = (policies?: AuthPolicy[]) => {
+    return dispatch(new UpdatePolicies(policies));
   }
 
-  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}>
+  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();
+     }
 
-              {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>
+    authenticationService.getServerReadyState().then(result =>{
+      setIsServerReady(result);
+    })
+  },[]);
 
-            </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 {
-      this.setState({
-        message: "Could not log in. Please check your credentials or ask your administrator for assistence.",
-        password: ""
-      })
+
+      if(!isServerReady){
+        const ready = await authenticationService.getServerReadyState();
+        if(ready){
+          setIsServerReady(true);
+        }else{
+          setMessage("Login is currently not possible. Please re-try in a few minutes. If the problem persists, ask your administrator for assistance.");
+        }
+  
+      }else{
+        setMessage("Could not log in. Please check your credentials or ask your administrator for assistance.");
+        setPassword("");
+      }
     }
   }
+  
+  const classes = styles();
+  const areProvidersAvailable = externalLoginProviders && externalLoginProviders.length > 0;
+
+  return (
+    <>
+      <CssBaseline />
+      <main className={classes.layout}>
+        <Paper className={classes.paper}>
+          <Avatar className={classes.avatar}>
+            <img src={loginIcon} alt="loginIcon" />
+          </Avatar>
+          <Typography variant="caption">Sign in</Typography>
+          <form className={classes.form}>
+            {areProvidersAvailable &&
+              <>
+                {
+                  externalLoginProviders!.map((provider, index) => (
+                    <Button
+                      aria-controls="externalLogin"
+                      aria-label={"external-login-identity-provider-" + (index + 1)}
+                      aria-haspopup="true"
+                      fullWidth
+                      variant="contained"
+                      color="inherit"
+                      className={classes.submit} onClick={() => { window.location = provider.loginUrl as any; }}>
+                      {provider.title}
+                    </Button>))
+                }
+                <div className={classes.lineContainer}>
+                  <span className={classes.thirdPartyDivider}>
+                    OR
+                  </span>
+                </div>
+              </>
+            }
+            <FormControl variant="standard" margin="normal" required fullWidth>
+              <InputLabel htmlFor="username">Username</InputLabel>
+              <Input id="username" name="username" autoComplete="username" autoFocus
+                disabled={isBusy}
+                value={username}
+                onChange={event => { setUsername(event.target.value); }} />
+            </FormControl>
+            <FormControl variant="standard" margin="normal" required fullWidth>
+              <InputLabel htmlFor="password">Password</InputLabel>
+              <Input
+                name="password"
+                type="password"
+                id="password"
+                autoComplete="current-password"
+                disabled={isBusy}
+                value={password}
+                onChange={event => { setPassword(event.target.value); }}
+              />
+            </FormControl>
+            <FormControl variant="standard" margin="normal" required fullWidth>
+              <InputLabel htmlFor="password">Domain</InputLabel>
+              <Input
+                name="scope"
+                type="scope"
+                id="scope"
+                disabled={isBusy}
+                value={scope}
+                onChange={event => { setScope(event.target.value); }}
+              />
+            </FormControl>
+            <Button
+              aria-label="login-button"
+              type="submit"
+              fullWidth
+              variant="contained"
+              color="inherit"
+              disabled={isBusy}
+              className={classes.submit}
+              onClick={onSignIn}
+            >
+              Sign in
+            </Button>
+
+          </form>
+          {message && <Alert severity="error">{message}</Alert>}
+        </Paper>
+      </main>
+    </>
+  );
 }
 
-export const Login = withStyles(styles)(withRouter(connect(mapProps, mapDispatch)(LoginComponent)));
+export const Login = withRouter(LoginComponent);
 export default Login;
\ No newline at end of file