mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2025-12-19 11:00:05 -06:00
Redesign idp (#174)
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -39,7 +39,7 @@ vendor-php
|
||||
.php-cs-fixer.cache
|
||||
suite-logs
|
||||
|
||||
# QA activity reports
|
||||
# QA activity reports
|
||||
tests/qa-activity-report/reports/
|
||||
|
||||
# drone CI is in .drone.star, do not let someone accidentally commit a local .drone.yml
|
||||
@@ -60,3 +60,4 @@ go.work
|
||||
go.work.sum
|
||||
.env
|
||||
.envrc
|
||||
.DS_Store
|
||||
|
||||
@@ -32,7 +32,9 @@ ci-node-generate: assets
|
||||
.PHONY: assets
|
||||
assets: pnpm-build \
|
||||
assets/identifier/static \
|
||||
assets/identifier/static/favicon.ico
|
||||
assets/identifier/static/favicon.ico \
|
||||
assets/identifier/static/icon-lilac.svg
|
||||
|
||||
|
||||
assets/identifier/static:
|
||||
mkdir -p assets/identifier/static
|
||||
@@ -41,6 +43,10 @@ assets/identifier/static:
|
||||
assets/identifier/static/favicon.ico:
|
||||
cp src/images/favicon.ico assets/identifier/static/favicon.ico
|
||||
|
||||
.PHONY: assets/identifier/static/icon-lilac.svg
|
||||
assets/identifier/static/icon-lilac.svg:
|
||||
cp src/images/icon-lilac.svg assets/identifier/static/icon-lilac.svg
|
||||
|
||||
.PHONY: pnpm-build
|
||||
pnpm-build: node_modules
|
||||
#pnpm lint #TODO: activate
|
||||
|
||||
@@ -1,69 +1,75 @@
|
||||
import React, { ReactElement, Suspense, lazy, useState, useEffect } from 'react';
|
||||
import React, {ReactElement, Suspense, lazy, useState, useEffect} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { MuiThemeProvider } from '@material-ui/core/styles';
|
||||
import { defaultTheme } from 'kpop/es/theme';
|
||||
import {MuiThemeProvider} from '@material-ui/core/styles';
|
||||
import {defaultTheme} from 'kpop/es/theme';
|
||||
|
||||
import 'kpop/static/css/base.css';
|
||||
import 'kpop/static/css/scrollbar.css';
|
||||
|
||||
import Spinner from './components/Spinner';
|
||||
import * as version from './version';
|
||||
import { OpenCloudContext } from './openCloudContext';
|
||||
import {OpenCloudContext} from './openCloudContext';
|
||||
|
||||
const LazyMain = lazy(() => import(/* webpackChunkName: "identifier-main" */ './Main'));
|
||||
|
||||
console.info(`Kopano Identifier build version: ${version.build}`); // eslint-disable-line no-console
|
||||
|
||||
const App = ({ bgImg }): ReactElement => {
|
||||
const [theme, setTheme] = useState(null);
|
||||
const [config, setConfig] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [theme, setTheme] = useState(null);
|
||||
const [config, setConfig] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const configResponse = await fetch('/config.json');
|
||||
const configData = await configResponse.json();
|
||||
setConfig(configData);
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const configResponse = await fetch('/config.json');
|
||||
const configData = await configResponse.json();
|
||||
setConfig(configData);
|
||||
|
||||
const themeResponse = await fetch(configData.theme);
|
||||
const themeData = await themeResponse.json();
|
||||
setTheme(themeData);
|
||||
} catch (error) {
|
||||
console.error('Error fetching config/theme data:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
const themeResponse = await fetch(configData.theme);
|
||||
const themeData = await themeResponse.json();
|
||||
setTheme(themeData);
|
||||
} catch (error) {
|
||||
console.error('Error fetching config/theme data:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, []);
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
|
||||
if (loading) {
|
||||
return <Spinner />;
|
||||
}
|
||||
if (loading) {
|
||||
return <Spinner/>;
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<OpenCloudContext.Provider value={{ theme, config }}>
|
||||
<div
|
||||
className='oc-login-bg'
|
||||
style={{ backgroundImage: bgImg ? `url(${bgImg})` : undefined }}
|
||||
>
|
||||
<MuiThemeProvider theme={defaultTheme}>
|
||||
<Suspense fallback={<Spinner />}>
|
||||
<LazyMain />
|
||||
</Suspense>
|
||||
</MuiThemeProvider>
|
||||
</div>
|
||||
</OpenCloudContext.Provider>
|
||||
);
|
||||
return (
|
||||
<OpenCloudContext.Provider value={{theme, config}}>
|
||||
<div
|
||||
className={`oc-login-bg ${bgImg ? 'oc-login-bg-image' : ''}`}
|
||||
style={{backgroundImage: bgImg ? `url(${bgImg})` : undefined}}
|
||||
>
|
||||
<MuiThemeProvider theme={defaultTheme}>
|
||||
<Suspense fallback={<Spinner/>}>
|
||||
<LazyMain/>
|
||||
</Suspense>
|
||||
</MuiThemeProvider>
|
||||
{!bgImg &&
|
||||
<img
|
||||
src={`${process.env.PUBLIC_URL}/static/icon-lilac.svg`}
|
||||
className={'oc-login-bg-icon'}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
</OpenCloudContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
App.propTypes = {
|
||||
bgImg: PropTypes.string
|
||||
bgImg: PropTypes.string
|
||||
};
|
||||
|
||||
export default App;
|
||||
|
||||
@@ -1,151 +1,194 @@
|
||||
/* additional css on top of kpop */
|
||||
@font-face {
|
||||
font-family: OpenCloud;
|
||||
src: url('./fonts/OpenCloud500-Regular.woff2') format('woff2');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-family: OpenCloud;
|
||||
src: url('./fonts/OpenCloud500-Regular.woff2') format('woff2');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: OpenCloud;
|
||||
src: url('./fonts/OpenCloud750-Bold.woff2') format('woff2');
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
font-family: OpenCloud;
|
||||
src: url('./fonts/OpenCloud750-Bold.woff2') format('woff2');
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
html {
|
||||
font-feature-settings: "cv11";
|
||||
font-feature-settings: "cv11";
|
||||
color: #20434f !important;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: OpenCloud, sans-serif;
|
||||
font-family: OpenCloud, sans-serif;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-weight: 600;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.oc-font-weight-light {
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.oc-login-bg {
|
||||
background-image: url(./images/background.png);
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
z-index: 0;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
background: #20434F;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.oc-login-bg-icon {
|
||||
position: fixed;
|
||||
right: -3vw;
|
||||
bottom: -3vh;
|
||||
height: 50vh;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.oc-login-bg-image {
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
#loader {
|
||||
/* NOTE(longsleep): White here needed because of the background image */
|
||||
color: white;
|
||||
text-shadow: #000 0 0 1px;
|
||||
/* NOTE(longsleep): White here needed because of the background image */
|
||||
color: white;
|
||||
text-shadow: #000 0 0 1px;
|
||||
}
|
||||
|
||||
.oc-logo {
|
||||
position: absolute;
|
||||
top: -130px;
|
||||
left: 50%;
|
||||
height: 80px;
|
||||
transform: translateX(-50%);
|
||||
height: 80px;
|
||||
z-index: 0;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.oc-progress {
|
||||
/* Needs to be important to overwrite material-ui */
|
||||
background-color: rgba(78, 133, 200, 0.8) !important;
|
||||
height: 4px;
|
||||
width: 100px;
|
||||
/* Needs to be important to overwrite material-ui */
|
||||
background-color: rgba(78, 133, 200, 0.8) !important;
|
||||
height: 4px;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.oc-progress > div {
|
||||
/* Needs to be important to overwrite material-ui */
|
||||
background-color: #4a76ac !important;
|
||||
/* Needs to be important to overwrite material-ui */
|
||||
background-color: #4a76ac !important;
|
||||
}
|
||||
|
||||
.oc-input {
|
||||
background-color: #fff;
|
||||
border: 1px solid #fff;
|
||||
border-radius: 3px;
|
||||
height: 40px;
|
||||
width: 300px;
|
||||
padding: 16px;
|
||||
box-sizing: border-box;
|
||||
font-size: 1rem;
|
||||
border: 1px solid #20434f;
|
||||
border-radius: 4px;
|
||||
height: 45px;
|
||||
width: 100%;
|
||||
padding: 16px;
|
||||
box-sizing: border-box;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.oc-label {
|
||||
color: #fff;
|
||||
display: inline-block;
|
||||
margin-bottom: 5px;
|
||||
display: block;
|
||||
margin-bottom: 12px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.oc-input.error {
|
||||
outline: none;
|
||||
border: 1px solid #fe4600;
|
||||
outline: none;
|
||||
border: 1px solid #fe4600;
|
||||
}
|
||||
|
||||
.MuiTypography-colorError {
|
||||
color: #fe4600 !important;
|
||||
color: #fe4600 !important;
|
||||
}
|
||||
|
||||
.oc-input:focus {
|
||||
outline: none;
|
||||
border: 1px solid #fff;
|
||||
outline: 2px solid #e2baff;
|
||||
border: 1px solid #fff;
|
||||
}
|
||||
|
||||
.oc-input + .oc-input {
|
||||
margin-top: 15px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.MuiTouchRipple-root {
|
||||
display: none !important;
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.oc-card {
|
||||
background: white;
|
||||
display: inline-flex;
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
.oc-card-body {
|
||||
padding: 40px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.oc-button {
|
||||
/* Needs to be important to overwrite material-ui */
|
||||
font-size: 1.0625rem !important;
|
||||
/* Needs to be important to overwrite material-ui */
|
||||
font-size: 1.0625rem !important;
|
||||
border-radius: 100px !important;
|
||||
box-shadow: none !important;
|
||||
font-weight: 700 !important;
|
||||
}
|
||||
|
||||
.oc-button-primary {
|
||||
/* Needs to be important to overwrite material-ui */
|
||||
width: 100%;
|
||||
background: #e2baff !important;
|
||||
border: 1px solid transparent !important;
|
||||
color: #19353f !important;
|
||||
/* Needs to be important to overwrite material-ui */
|
||||
width: 100%;
|
||||
background: #e2baff !important;
|
||||
border-radius: 100px !important;
|
||||
color: #20434f !important;
|
||||
box-shadow: none !important;
|
||||
font-weight: 700 !important;
|
||||
}
|
||||
|
||||
.oc-checkbox-dark svg {
|
||||
/* Needs to be important to overwrite material-ui */
|
||||
fill: white !important;
|
||||
.oc-button-secondary {
|
||||
/* Needs to be important to overwrite material-ui */
|
||||
width: 100%;
|
||||
border-radius: 100px !important;
|
||||
background: #F1F3F4 !important;
|
||||
color: #20434f !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.oc-footer-message {
|
||||
color: white;
|
||||
padding: 10px;
|
||||
font-size: 0.8rem;
|
||||
color: white;
|
||||
padding: 10px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
.oc-logo {
|
||||
height: 60px;
|
||||
top: -90px;
|
||||
}
|
||||
.oc-logo {
|
||||
height: 60px;
|
||||
top: -90px;
|
||||
}
|
||||
|
||||
.oc-login-bg-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.oc-card {
|
||||
min-width: 50vw !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 600px) {
|
||||
.oc-card {
|
||||
width: 100vw !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Helpers */
|
||||
.oc-mt-l {
|
||||
margin-top: 24px !important;
|
||||
margin-top: 24px !important;
|
||||
}
|
||||
|
||||
.oc-mb-m {
|
||||
margin-bottom: 20px !important;
|
||||
}
|
||||
|
||||
.oc-light {
|
||||
color: #fff !important;
|
||||
margin-bottom: 20px !important;
|
||||
}
|
||||
|
||||
.oc-login-form div:not(:last-of-type) {
|
||||
margin-bottom: 15px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
|
||||
@@ -154,14 +197,14 @@ strong {
|
||||
* Used to hide an element visually, but keeping it accessible for accessibility tools.
|
||||
*/
|
||||
.oc-invisible-sr {
|
||||
border: 0 !important;
|
||||
clip: rect(1px, 1px, 1px, 1px) !important;
|
||||
height: 1px !important;
|
||||
overflow: hidden !important;
|
||||
padding: 0 !important;
|
||||
/* Need to make sure we override any existing styles. */
|
||||
position: absolute !important;
|
||||
top: 0;
|
||||
white-space: nowrap;
|
||||
width: 1px !important;
|
||||
border: 0 !important;
|
||||
clip: rect(1px, 1px, 1px, 1px) !important;
|
||||
height: 1px !important;
|
||||
overflow: hidden !important;
|
||||
padding: 0 !important;
|
||||
/* Need to make sure we override any existing styles. */
|
||||
position: absolute !important;
|
||||
top: 0;
|
||||
white-space: nowrap;
|
||||
width: 1px !important;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ class Loading extends React.PureComponent {
|
||||
))}
|
||||
{renderIf(error !== null)(() => (
|
||||
<div>
|
||||
<Typography className="oc-light" variant="h5" gutterBottom align="center">
|
||||
<Typography variant="h5" gutterBottom align="center">
|
||||
{t("konnect.loading.error.headline", "Failed to connect to server")}
|
||||
</Typography>
|
||||
<Typography align="center" color="error">
|
||||
|
||||
@@ -15,10 +15,13 @@ const styles = theme => ({
|
||||
root: {
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
zIndex: 999
|
||||
},
|
||||
content: {
|
||||
position: 'relative',
|
||||
width: '100%'
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center'
|
||||
},
|
||||
actions: {
|
||||
marginTop: -40,
|
||||
@@ -27,8 +30,6 @@ const styles = theme => ({
|
||||
paddingRight: theme.spacing(3)
|
||||
},
|
||||
wrapper: {
|
||||
width: '100%',
|
||||
maxWidth: 300,
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
alignItems: 'center'
|
||||
@@ -59,7 +60,11 @@ const ResponsiveScreen = (props) => {
|
||||
<div className={classes.wrapper}>
|
||||
<div className={classes.content}>
|
||||
{logo}
|
||||
{content}
|
||||
<div className={'oc-card'}>
|
||||
<div className={'oc-card-body'}>
|
||||
{content}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer className="oc-footer-message">
|
||||
|
||||
@@ -67,9 +67,8 @@ const ScopesList = ({scopes, meta, classes, ...rest}) => {
|
||||
checked
|
||||
disableRipple
|
||||
disabled
|
||||
className="oc-checkbox-dark"
|
||||
/>
|
||||
<ListItemText primary={label} className="oc-light" />
|
||||
<ListItemText primary={label} />
|
||||
</ListItem>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -61,10 +61,10 @@ class Chooseaccount extends React.PureComponent {
|
||||
|
||||
return (
|
||||
<DialogContent className={classes.content}>
|
||||
<Typography variant="h5" component="h3" className="oc-light">
|
||||
<Typography variant="h5" component="h3">
|
||||
{t("konnect.chooseaccount.headline", "Choose an account")}
|
||||
</Typography>
|
||||
<Typography variant="subtitle1" className={classes.subHeader + " oc-light"}>
|
||||
<Typography variant="subtitle1" className={classes.subHeader}>
|
||||
{t("konnect.chooseaccount.subHeader", "to sign in")}
|
||||
</Typography>
|
||||
|
||||
@@ -77,7 +77,7 @@ class Chooseaccount extends React.PureComponent {
|
||||
disabled={!!loading}
|
||||
onClick={(event) => this.logon(event)}
|
||||
><ListItemAvatar><Avatar>{username.substr(0, 1)}</Avatar></ListItemAvatar>
|
||||
<ListItemText className="oc-light" primary={username} />
|
||||
<ListItemText primary={username} />
|
||||
</ListItem>
|
||||
<ListItem
|
||||
button
|
||||
@@ -92,7 +92,6 @@ class Chooseaccount extends React.PureComponent {
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
className="oc-light"
|
||||
primary={
|
||||
t("konnect.chooseaccount.useOther.label", "Use another account")
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import { withTranslation, Trans } from 'react-i18next';
|
||||
import {withTranslation, Trans} from 'react-i18next';
|
||||
|
||||
import renderIf from 'render-if';
|
||||
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import {withStyles} from '@material-ui/core/styles';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import BaseTooltip from '@material-ui/core/Tooltip';
|
||||
import CircularProgress from '@material-ui/core/CircularProgress';
|
||||
@@ -15,46 +15,54 @@ import Typography from '@material-ui/core/Typography';
|
||||
import DialogActions from '@material-ui/core/DialogActions';
|
||||
import DialogContent from '@material-ui/core/DialogContent';
|
||||
|
||||
import { executeConsent, advanceLogonFlow, receiveValidateLogon } from '../../actions/login';
|
||||
import { ErrorMessage } from '../../errors';
|
||||
import { REQUEST_CONSENT_ALLOW } from '../../actions/types';
|
||||
import {executeConsent, advanceLogonFlow, receiveValidateLogon} from '../../actions/login';
|
||||
import {ErrorMessage} from '../../errors';
|
||||
import {REQUEST_CONSENT_ALLOW} from '../../actions/types';
|
||||
import ClientDisplayName from '../../components/ClientDisplayName';
|
||||
import ScopesList from '../../components/ScopesList';
|
||||
|
||||
const styles = theme => ({
|
||||
button: {
|
||||
margin: theme.spacing(1),
|
||||
minWidth: 100
|
||||
},
|
||||
buttonProgress: {
|
||||
color: green[500],
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
marginTop: -12,
|
||||
marginLeft: -12
|
||||
},
|
||||
subHeader: {
|
||||
marginBottom: theme.spacing(2)
|
||||
},
|
||||
scopesList: {
|
||||
marginBottom: theme.spacing(2)
|
||||
},
|
||||
wrapper: {
|
||||
marginTop: theme.spacing(2),
|
||||
position: 'relative',
|
||||
display: 'inline-block'
|
||||
},
|
||||
message: {
|
||||
marginTop: theme.spacing(2),
|
||||
marginBottom: theme.spacing(2)
|
||||
}
|
||||
button: {
|
||||
margin: theme.spacing(1),
|
||||
minWidth: 100
|
||||
},
|
||||
buttonProgress: {
|
||||
color: green[500],
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
marginTop: -12,
|
||||
marginLeft: -12
|
||||
},
|
||||
header: {
|
||||
textAlign: 'center',
|
||||
margin: 0,
|
||||
},
|
||||
subHeader: {
|
||||
textAlign: 'center',
|
||||
marginBottom: theme.spacing(2)
|
||||
},
|
||||
scopesList: {
|
||||
marginBottom: theme.spacing(2)
|
||||
},
|
||||
wrapper: {
|
||||
marginTop: theme.spacing(2),
|
||||
position: 'relative',
|
||||
display: 'inline-block'
|
||||
},
|
||||
dialogActions: {
|
||||
gap: theme.spacing(1)
|
||||
},
|
||||
message: {
|
||||
marginTop: theme.spacing(2),
|
||||
marginBottom: theme.spacing(2)
|
||||
}
|
||||
});
|
||||
|
||||
const Tooltip = ({children, ...other } = {}) => {
|
||||
// Ensures that there is only a single child for the tooltip element to
|
||||
// make it compatible with the Trans component.
|
||||
return <BaseTooltip {...other}><span>{children}</span></BaseTooltip>;
|
||||
const Tooltip = ({children, ...other} = {}) => {
|
||||
// Ensures that there is only a single child for the tooltip element to
|
||||
// make it compatible with the Trans component.
|
||||
return <BaseTooltip {...other}><span>{children}</span></BaseTooltip>;
|
||||
}
|
||||
|
||||
Tooltip.propTypes = {
|
||||
@@ -62,134 +70,137 @@ Tooltip.propTypes = {
|
||||
};
|
||||
|
||||
class Consent extends React.PureComponent {
|
||||
componentDidMount() {
|
||||
const { dispatch, hello, history, client } = this.props;
|
||||
if ((!hello || !hello.state || !client) && history.action !== 'PUSH') {
|
||||
history.replace(`/identifier${history.location.search}${history.location.hash}`);
|
||||
componentDidMount() {
|
||||
const {dispatch, hello, history, client} = this.props;
|
||||
if ((!hello || !hello.state || !client) && history.action !== 'PUSH') {
|
||||
history.replace(`/identifier${history.location.search}${history.location.hash}`);
|
||||
}
|
||||
|
||||
dispatch(receiveValidateLogon({})); // XXX(longsleep): hack to reset loading and errors.
|
||||
}
|
||||
|
||||
dispatch(receiveValidateLogon({})); // XXX(longsleep): hack to reset loading and errors.
|
||||
}
|
||||
action = (allow = false, scopes = {}) => (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
action = (allow=false, scopes={}) => (event) => {
|
||||
event.preventDefault();
|
||||
if (allow === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (allow === undefined) {
|
||||
return;
|
||||
// Convert all scopes which are true to a scope value.
|
||||
const scope = Object.keys(scopes).filter(scope => {
|
||||
return !!scopes[scope];
|
||||
}).join(' ');
|
||||
|
||||
const {dispatch, history} = this.props;
|
||||
dispatch(executeConsent(allow, scope)).then((response) => {
|
||||
if (response.success) {
|
||||
dispatch(advanceLogonFlow(response.success, history, true, {konnect: response.state}));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Convert all scopes which are true to a scope value.
|
||||
const scope = Object.keys(scopes).filter(scope => {
|
||||
return !!scopes[scope];
|
||||
}).join(' ');
|
||||
render() {
|
||||
const {classes, loading, hello, errors, client, t} = this.props;
|
||||
|
||||
const { dispatch, history } = this.props;
|
||||
dispatch(executeConsent(allow, scope)).then((response) => {
|
||||
if (response.success) {
|
||||
dispatch(advanceLogonFlow(response.success, history, true, {konnect: response.state}));
|
||||
}
|
||||
});
|
||||
}
|
||||
const scopes = hello.details.scopes || {};
|
||||
const meta = hello.details.meta || {};
|
||||
|
||||
render() {
|
||||
const { classes, loading, hello, errors, client, t } = this.props;
|
||||
return (
|
||||
<DialogContent>
|
||||
<h1 className={classes.header}>
|
||||
{t("konnect.consent.headline", "Hi {{displayName}}", {displayName: hello.displayName})}
|
||||
</h1>
|
||||
<Typography variant="subtitle1" className={classes.subHeader + " oc-mb-m"}>
|
||||
{hello.username}
|
||||
</Typography>
|
||||
|
||||
const scopes = hello.details.scopes || {};
|
||||
const meta = hello.details.meta || {};
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
<Trans t={t} i18nKey="konnect.consent.message">
|
||||
<Tooltip
|
||||
placement="bottom"
|
||||
title={t("konnect.consent.tooltip.client", 'Clicking "Allow" will redirect you to: {{redirectURI}}', {redirectURI: client.redirect_uri})}
|
||||
>
|
||||
<span className={'oc-font-weight-light'}><ClientDisplayName client={client}/></span>
|
||||
</Tooltip> wants to
|
||||
</Trans>
|
||||
</Typography>
|
||||
<ScopesList dense disablePadding className={classes.scopesList} scopes={scopes}
|
||||
meta={meta.scopes}></ScopesList>
|
||||
|
||||
return (
|
||||
<DialogContent>
|
||||
<Typography variant="h5" component="h3" className="oc-light">
|
||||
{t("konnect.consent.headline", "Hi {{displayName}}", { displayName: hello.displayName })}
|
||||
</Typography>
|
||||
<Typography variant="subtitle1" className={classes.subHeader + " oc-light oc-mb-m"}>
|
||||
{hello.username}
|
||||
</Typography>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
<Trans t={t} i18nKey="konnect.consent.question">
|
||||
Allow <span className={'oc-font-weight-light'}><ClientDisplayName client={client}/></span> to do
|
||||
this?
|
||||
</Trans>
|
||||
</Typography>
|
||||
<Typography>
|
||||
{t("konnect.consent.consequence", "By clicking Allow, you allow this app to use your information.")}
|
||||
</Typography>
|
||||
|
||||
<Typography variant="subtitle1" gutterBottom className="oc-light">
|
||||
<Trans t={t} i18nKey="konnect.consent.message">
|
||||
<Tooltip
|
||||
placement="bottom"
|
||||
title={t("konnect.consent.tooltip.client", 'Clicking "Allow" will redirect you to: {{redirectURI}}', { redirectURI: client.redirect_uri })}
|
||||
>
|
||||
<em><ClientDisplayName client={client}/></em>
|
||||
</Tooltip> wants to
|
||||
</Trans>
|
||||
</Typography>
|
||||
<ScopesList dense disablePadding className={classes.scopesList} scopes={scopes} meta={meta.scopes}></ScopesList>
|
||||
<form action="" onSubmit={this.action(undefined, scopes)}>
|
||||
<DialogActions className={classes.dialogActions}>
|
||||
<div className={classes.wrapper}>
|
||||
<Button
|
||||
color="secondary"
|
||||
className={classes.button + ' oc-button-secondary'}
|
||||
disabled={!!loading}
|
||||
onClick={this.action(false, scopes)}
|
||||
>
|
||||
{t("konnect.consent.cancelButton.label", "Cancel")}
|
||||
</Button>
|
||||
{(loading && loading !== REQUEST_CONSENT_ALLOW) &&
|
||||
<CircularProgress size={24} className={classes.buttonProgress}/>}
|
||||
</div>
|
||||
<div className={classes.wrapper}>
|
||||
<Button
|
||||
type="submit"
|
||||
color="primary"
|
||||
variant="contained"
|
||||
className="oc-button-primary"
|
||||
disabled={!!loading}
|
||||
onClick={this.action(true, scopes)}
|
||||
>
|
||||
{t("konnect.consent.allowButton.label", "Allow")}
|
||||
</Button>
|
||||
{loading === REQUEST_CONSENT_ALLOW &&
|
||||
<CircularProgress size={24} className={classes.buttonProgress}/>}
|
||||
</div>
|
||||
</DialogActions>
|
||||
|
||||
<Typography variant="subtitle1" gutterBottom className="oc-light">
|
||||
<Trans t={t} i18nKey="konnect.consent.question">
|
||||
Allow <em><ClientDisplayName client={client}/></em> to do this?
|
||||
</Trans>
|
||||
</Typography>
|
||||
<Typography className="oc-light">
|
||||
{t("konnect.consent.consequence", "By clicking Allow, you allow this app to use your information.")}
|
||||
</Typography>
|
||||
|
||||
<form action="" onSubmit={this.action(undefined, scopes)}>
|
||||
<DialogActions>
|
||||
<div className={classes.wrapper}>
|
||||
<Button
|
||||
color="secondary"
|
||||
className={classes.button}
|
||||
disabled={!!loading}
|
||||
onClick={this.action(false, scopes)}
|
||||
>
|
||||
{t("konnect.consent.cancelButton.label", "Cancel")}
|
||||
</Button>
|
||||
{(loading && loading !== REQUEST_CONSENT_ALLOW) &&
|
||||
<CircularProgress size={24} className={classes.buttonProgress} />}
|
||||
</div>
|
||||
<div className={classes.wrapper}>
|
||||
<Button
|
||||
type="submit"
|
||||
color="primary"
|
||||
variant="contained"
|
||||
className="oc-button-primary"
|
||||
disabled={!!loading}
|
||||
onClick={this.action(true, scopes)}
|
||||
>
|
||||
{t("konnect.consent.allowButton.label", "Allow")}
|
||||
</Button>
|
||||
{loading === REQUEST_CONSENT_ALLOW && <CircularProgress size={24} className={classes.buttonProgress} />}
|
||||
</div>
|
||||
</DialogActions>
|
||||
|
||||
{renderIf(errors.http)(() => (
|
||||
<Typography variant="subtitle2" color="error" className={classes.message}>
|
||||
<ErrorMessage error={errors.http}></ErrorMessage>
|
||||
</Typography>
|
||||
))}
|
||||
</form>
|
||||
</DialogContent>
|
||||
);
|
||||
}
|
||||
{renderIf(errors.http)(() => (
|
||||
<Typography variant="subtitle2" color="error" className={classes.message}>
|
||||
<ErrorMessage error={errors.http}></ErrorMessage>
|
||||
</Typography>
|
||||
))}
|
||||
</form>
|
||||
</DialogContent>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Consent.propTypes = {
|
||||
classes: PropTypes.object.isRequired,
|
||||
t: PropTypes.func.isRequired,
|
||||
classes: PropTypes.object.isRequired,
|
||||
t: PropTypes.func.isRequired,
|
||||
|
||||
loading: PropTypes.string.isRequired,
|
||||
errors: PropTypes.object.isRequired,
|
||||
hello: PropTypes.object,
|
||||
client: PropTypes.object.isRequired,
|
||||
loading: PropTypes.string.isRequired,
|
||||
errors: PropTypes.object.isRequired,
|
||||
hello: PropTypes.object,
|
||||
client: PropTypes.object.isRequired,
|
||||
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
history: PropTypes.object.isRequired
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
history: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const { hello } = state.common;
|
||||
const { loading, errors } = state.login;
|
||||
const {hello} = state.common;
|
||||
const {loading, errors} = state.login;
|
||||
|
||||
return {
|
||||
loading: loading,
|
||||
errors,
|
||||
hello,
|
||||
client: hello.details.client || {}
|
||||
};
|
||||
return {
|
||||
loading: loading,
|
||||
errors,
|
||||
hello,
|
||||
client: hello.details.client || {}
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(withStyles(styles)(withTranslation()(Consent)));
|
||||
|
||||
@@ -1,207 +1,209 @@
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import React, {useEffect, useMemo} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
|
||||
import renderIf from 'render-if';
|
||||
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import {withStyles} from '@material-ui/core/styles';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import CircularProgress from '@material-ui/core/CircularProgress';
|
||||
import green from '@material-ui/core/colors/green';
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import DialogActions from '@material-ui/core/DialogActions';
|
||||
import DialogContent from '@material-ui/core/DialogContent';
|
||||
import Link from '@material-ui/core/Link';
|
||||
|
||||
import TextInput from '../../components/TextInput'
|
||||
|
||||
import { updateInput, executeLogonIfFormValid, advanceLogonFlow } from '../../actions/login';
|
||||
import { ErrorMessage } from '../../errors';
|
||||
import {updateInput, executeLogonIfFormValid, advanceLogonFlow} from '../../actions/login';
|
||||
import {ErrorMessage} from '../../errors';
|
||||
|
||||
const styles = theme => ({
|
||||
buttonProgress: {
|
||||
color: green[500],
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
marginTop: -12,
|
||||
marginLeft: -12
|
||||
},
|
||||
subHeader: {
|
||||
marginBottom: theme.spacing(3)
|
||||
},
|
||||
wrapper: {
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
textAlign: 'center'
|
||||
},
|
||||
message: {
|
||||
marginTop: 5,
|
||||
marginBottom: 5
|
||||
}
|
||||
buttonProgress: {
|
||||
color: green[500],
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
marginTop: -12,
|
||||
marginLeft: -12
|
||||
},
|
||||
main: {
|
||||
width: '100%'
|
||||
},
|
||||
header: {
|
||||
textAlign: 'center',
|
||||
marginTop: 0,
|
||||
marginBottom: theme.spacing(6)
|
||||
},
|
||||
wrapper: {
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
textAlign: 'center'
|
||||
},
|
||||
message: {
|
||||
marginTop: 5,
|
||||
marginBottom: 5
|
||||
}
|
||||
});
|
||||
|
||||
function Login(props) {
|
||||
const {
|
||||
hello,
|
||||
query,
|
||||
dispatch,
|
||||
history,
|
||||
loading,
|
||||
errors,
|
||||
classes,
|
||||
username,
|
||||
password,
|
||||
passwordResetLink,
|
||||
} = props;
|
||||
const {
|
||||
hello,
|
||||
query,
|
||||
dispatch,
|
||||
history,
|
||||
loading,
|
||||
errors,
|
||||
classes,
|
||||
username,
|
||||
password,
|
||||
passwordResetLink,
|
||||
} = props;
|
||||
|
||||
const { t } = useTranslation();
|
||||
const loginFailed = errors.http;
|
||||
const hasError = errors.http || errors.username || errors.password;
|
||||
const errorMessage = errors.http
|
||||
? <ErrorMessage error={errors.http}></ErrorMessage>
|
||||
: (errors.username
|
||||
? <ErrorMessage error={errors.username}></ErrorMessage>
|
||||
: <ErrorMessage error={errors.password}></ErrorMessage>);
|
||||
const extraPropsUsername = {
|
||||
"aria-invalid" : (errors.username || errors.http) ? 'true' : 'false'
|
||||
};
|
||||
const extraPropsPassword = {
|
||||
"aria-invalid" : (errors.password || errors.http) ? 'true' : 'false',
|
||||
};
|
||||
const {t} = useTranslation();
|
||||
const loginFailed = errors.http;
|
||||
const hasError = errors.http || errors.username || errors.password;
|
||||
const errorMessage = errors.http
|
||||
? <ErrorMessage error={errors.http}></ErrorMessage>
|
||||
: (errors.username
|
||||
? <ErrorMessage error={errors.username}></ErrorMessage>
|
||||
: <ErrorMessage error={errors.password}></ErrorMessage>);
|
||||
const extraPropsUsername = {
|
||||
"aria-invalid": (errors.username || errors.http) ? 'true' : 'false'
|
||||
};
|
||||
const extraPropsPassword = {
|
||||
"aria-invalid": (errors.password || errors.http) ? 'true' : 'false',
|
||||
};
|
||||
|
||||
if(errors.username || errors.http){
|
||||
extraPropsUsername['extraClassName'] = 'error';
|
||||
extraPropsUsername['aria-describedby'] = 'oc-login-error-message';
|
||||
}
|
||||
|
||||
if(errors.password || errors.http){
|
||||
extraPropsPassword['extraClassName'] = 'error';
|
||||
extraPropsPassword['aria-describedby'] = 'oc-login-error-message';
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (hello && hello.state && history.action !== 'PUSH') {
|
||||
if (!query.prompt || query.prompt.indexOf('select_account') === -1) {
|
||||
dispatch(advanceLogonFlow(true, history));
|
||||
return;
|
||||
}
|
||||
|
||||
history.replace(`/chooseaccount${history.location.search}${history.location.hash}`);
|
||||
return;
|
||||
if (errors.username || errors.http) {
|
||||
extraPropsUsername['extraClassName'] = 'error';
|
||||
extraPropsUsername['aria-describedby'] = 'oc-login-error-message';
|
||||
}
|
||||
});
|
||||
|
||||
const handleChange = (name) => (event) => {
|
||||
dispatch(updateInput(name, event.target.value));
|
||||
};
|
||||
if (errors.password || errors.http) {
|
||||
extraPropsPassword['extraClassName'] = 'error';
|
||||
extraPropsPassword['aria-describedby'] = 'oc-login-error-message';
|
||||
}
|
||||
|
||||
const handleNextClick = (event) => {
|
||||
event.preventDefault();
|
||||
useEffect(() => {
|
||||
if (hello && hello.state && history.action !== 'PUSH') {
|
||||
if (!query.prompt || query.prompt.indexOf('select_account') === -1) {
|
||||
dispatch(advanceLogonFlow(true, history));
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(executeLogonIfFormValid(username, password, false)).then((response) => {
|
||||
if (response.success) {
|
||||
dispatch(advanceLogonFlow(response.success, history));
|
||||
}
|
||||
history.replace(`/chooseaccount${history.location.search}${history.location.hash}`);
|
||||
return;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const usernamePlaceHolder = useMemo(() => {
|
||||
if (hello?.details?.branding?.usernameHintText ) {
|
||||
switch (hello.details.branding.usernameHintText) {
|
||||
case "Username":
|
||||
break;
|
||||
case "Email":
|
||||
return t("konnect.login.usernameField.placeholder.email", "Email");
|
||||
case "Identity":
|
||||
return t("konnect.login.usernameField.placeholder.identity", "Identity");
|
||||
default:
|
||||
return hello.details.branding.usernameHintText;
|
||||
}
|
||||
}
|
||||
const handleChange = (name) => (event) => {
|
||||
dispatch(updateInput(name, event.target.value));
|
||||
};
|
||||
|
||||
return t("konnect.login.usernameField.placeholder.username", "Username");
|
||||
}, [hello, t]);
|
||||
const handleNextClick = (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="oc-invisible-sr"> Login </h1>
|
||||
<form action="" className="oc-login-form" onSubmit={(event) => handleNextClick(event)}>
|
||||
<TextInput
|
||||
autoFocus
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
value={username}
|
||||
onChange={handleChange('username')}
|
||||
autoComplete="kopano-account username"
|
||||
placeholder={t("konnect.login.usernameField.label", "Username")}
|
||||
label={t("konnect.login.usernameField.label", "Username")}
|
||||
id="oc-login-username"
|
||||
{...extraPropsUsername}
|
||||
/>
|
||||
<TextInput
|
||||
type="password"
|
||||
margin="normal"
|
||||
onChange={handleChange('password')}
|
||||
autoComplete="kopano-account current-password"
|
||||
placeholder={t("konnect.login.passwordField.label", "Password")}
|
||||
label={t("konnect.login.passwordField.label", "Password")}
|
||||
id="oc-login-password"
|
||||
{...extraPropsPassword}
|
||||
/>
|
||||
{hasError && <Typography id="oc-login-error-message" variant="subtitle2" component="span" color="error" className={classes.message}>{errorMessage}</Typography>}
|
||||
<div className={classes.wrapper}>
|
||||
{loginFailed && passwordResetLink && <Link id="oc-login-password-reset" href={passwordResetLink} variant="subtitle2">{"Reset password?"}</Link>}
|
||||
<Button
|
||||
type="submit"
|
||||
color="primary"
|
||||
variant="contained"
|
||||
className="oc-button-primary oc-mt-l"
|
||||
disabled={!!loading}
|
||||
onClick={handleNextClick}
|
||||
>
|
||||
{t("konnect.login.nextButton.label", "Log in")}
|
||||
</Button>
|
||||
{loading && <CircularProgress size={24} className={classes.buttonProgress} />}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
dispatch(executeLogonIfFormValid(username, password, false)).then((response) => {
|
||||
if (response.success) {
|
||||
dispatch(advanceLogonFlow(response.success, history));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const usernamePlaceHolder = useMemo(() => {
|
||||
if (hello?.details?.branding?.usernameHintText) {
|
||||
switch (hello.details.branding.usernameHintText) {
|
||||
case "Username":
|
||||
break;
|
||||
case "Email":
|
||||
return t("konnect.login.usernameField.placeholder.email", "Email");
|
||||
case "Identity":
|
||||
return t("konnect.login.usernameField.placeholder.identity", "Identity");
|
||||
default:
|
||||
return hello.details.branding.usernameHintText;
|
||||
}
|
||||
}
|
||||
|
||||
return t("konnect.login.usernameField.placeholder.username", "Username");
|
||||
}, [hello, t]);
|
||||
|
||||
return (
|
||||
<div className={classes.main}>
|
||||
<h1 className={classes.header}> {t("konnect.login.headline", "Sign in")}</h1>
|
||||
<form action="" className="oc-login-form" onSubmit={(event) => handleNextClick(event)}>
|
||||
<TextInput
|
||||
autoFocus
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
value={username}
|
||||
onChange={handleChange('username')}
|
||||
autoComplete="kopano-account username"
|
||||
placeholder={t("konnect.login.usernameField.label", "Username")}
|
||||
label={t("konnect.login.usernameField.label", "Username")}
|
||||
id="oc-login-username"
|
||||
{...extraPropsUsername}
|
||||
/>
|
||||
<TextInput
|
||||
type="password"
|
||||
margin="normal"
|
||||
onChange={handleChange('password')}
|
||||
autoComplete="kopano-account current-password"
|
||||
placeholder={t("konnect.login.passwordField.label", "Password")}
|
||||
label={t("konnect.login.passwordField.label", "Password")}
|
||||
id="oc-login-password"
|
||||
{...extraPropsPassword}
|
||||
/>
|
||||
{hasError && <Typography id="oc-login-error-message" variant="subtitle2" component="span" color="error"
|
||||
className={classes.message}>{errorMessage}</Typography>}
|
||||
<div className={classes.wrapper}>
|
||||
{loginFailed && passwordResetLink && <Link id="oc-login-password-reset" href={passwordResetLink}
|
||||
variant="subtitle2">{"Reset password?"}</Link>}
|
||||
<Button
|
||||
type="submit"
|
||||
color="primary"
|
||||
variant="contained"
|
||||
className="oc-button-primary oc-mt-l"
|
||||
disabled={!!loading}
|
||||
onClick={handleNextClick}
|
||||
>
|
||||
{t("konnect.login.nextButton.label", "Log in")}
|
||||
</Button>
|
||||
{loading && <CircularProgress size={24} className={classes.buttonProgress}/>}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Login.propTypes = {
|
||||
classes: PropTypes.object.isRequired,
|
||||
classes: PropTypes.object.isRequired,
|
||||
|
||||
loading: PropTypes.string.isRequired,
|
||||
username: PropTypes.string.isRequired,
|
||||
password: PropTypes.string.isRequired,
|
||||
passwordResetLink: PropTypes.string.isRequired,
|
||||
errors: PropTypes.object.isRequired,
|
||||
branding: PropTypes.object,
|
||||
hello: PropTypes.object,
|
||||
query: PropTypes.object.isRequired,
|
||||
loading: PropTypes.string.isRequired,
|
||||
username: PropTypes.string.isRequired,
|
||||
password: PropTypes.string.isRequired,
|
||||
passwordResetLink: PropTypes.string.isRequired,
|
||||
errors: PropTypes.object.isRequired,
|
||||
branding: PropTypes.object,
|
||||
hello: PropTypes.object,
|
||||
query: PropTypes.object.isRequired,
|
||||
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
history: PropTypes.object.isRequired
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
history: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const { loading, username, password, errors} = state.login;
|
||||
const { branding, hello, query, passwordResetLink } = state.common;
|
||||
const {loading, username, password, errors} = state.login;
|
||||
const {branding, hello, query, passwordResetLink} = state.common;
|
||||
|
||||
return {
|
||||
loading,
|
||||
username,
|
||||
password,
|
||||
errors,
|
||||
branding,
|
||||
hello,
|
||||
query,
|
||||
passwordResetLink
|
||||
};
|
||||
return {
|
||||
loading,
|
||||
username,
|
||||
password,
|
||||
errors,
|
||||
branding,
|
||||
hello,
|
||||
query,
|
||||
passwordResetLink
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(withStyles(styles)(Login));
|
||||
|
||||
@@ -29,14 +29,14 @@ class Welcomescreen extends React.PureComponent {
|
||||
const loading = hello === null;
|
||||
return (
|
||||
<ResponsiveScreen loading={loading} branding={branding}>
|
||||
<Typography variant="h5" component="h3" className="oc-light" >
|
||||
<Typography variant="h5" component="h3">
|
||||
{t("konnect.welcome.headline", "Welcome {{displayName}}", {displayName: hello.displayName})}
|
||||
</Typography>
|
||||
<Typography variant="subtitle1" className={classes.subHeader + " oc-light"}>
|
||||
<Typography variant="subtitle1" className={classes.subHeader}>
|
||||
{hello.username}
|
||||
</Typography>
|
||||
|
||||
<Typography gutterBottom className="oc-light">
|
||||
<Typography gutterBottom>
|
||||
{t("konnect.welcome.message", "You are signed in - awesome!")}
|
||||
</Typography>
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB |
10
services/idp/src/images/icon-lilac.svg
Normal file
10
services/idp/src/images/icon-lilac.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="2.83 2.84 21.45 27.82">
|
||||
<polygon
|
||||
points="13.55 23.12 14.72 22.45 14.72 17.57 18.92 15.14 18.92 13.8 17.75 13.12 13.53 15.56 9.36 13.16 8.19 13.83 8.19 15.18 12.39 17.6 12.39 22.45 13.55 23.12"
|
||||
fill="#e2baff"/>
|
||||
<polygon points="24.28 9.02 13.56 2.84 13.56 2.84 13.56 2.84 2.83 9.02 2.83 11.72 13.56 5.53 24.28 11.72 24.28 9.02"
|
||||
fill="#e2baff"/>
|
||||
<polygon
|
||||
points="24.28 21.78 13.56 27.97 2.83 21.78 2.83 24.48 13.56 30.66 13.56 30.66 13.56 30.66 24.28 24.48 24.28 21.78"
|
||||
fill="#e2baff"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 613 B |
Reference in New Issue
Block a user