diff options
Diffstat (limited to 'sdnr/wt/odlux/framework/src/views')
-rw-r--r-- | sdnr/wt/odlux/framework/src/views/about.tsx | 78 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/src/views/frame.tsx | 143 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/src/views/home.tsx | 33 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/src/views/login.tsx | 320 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/src/views/settings.tsx | 45 |
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 |