1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

Fix/standalone pages (#300)

* feat: change layout

* fix: standalone banner styling

* fix: change styling for banner

* fix: login page

* fix: standalone pages

* fix: page tweaks

* fix: center text

* refactor: new user page

* refactor: remove uneccesary markup

* refactor: remove secondary actions from hosted

* fix: remove authdetails blob

* refactor: remove unused import

* fix: move overflow

* fix: add breakpoint to forgotten password
This commit is contained in:
Fredrik Strand Oseberg 2021-05-21 14:01:28 +02:00 committed by GitHub
parent 998cdf98ab
commit bd93c5d131
31 changed files with 490 additions and 365 deletions

View File

@ -60,6 +60,7 @@
>
<g
id="button"
transform="translate(4.000000, 4.000000)"
filter="url(#filter-1)"
>
@ -74,22 +75,22 @@
<path
d="M17.64,9.20454545 C17.64,8.56636364 17.5827273,7.95272727 17.4763636,7.36363636 L9,7.36363636 L9,10.845 L13.8436364,10.845 C13.635,11.97 13.0009091,12.9231818 12.0477273,13.5613636 L12.0477273,15.8195455 L14.9563636,15.8195455 C16.6581818,14.2527273 17.64,11.9454545 17.64,9.20454545 L17.64,9.20454545 Z"
id="Shape"
fill="#4285F4"
fill="#000"
/>
<path
d="M9,18 C11.43,18 13.4672727,17.1940909 14.9563636,15.8195455 L12.0477273,13.5613636 C11.2418182,14.1013636 10.2109091,14.4204545 9,14.4204545 C6.65590909,14.4204545 4.67181818,12.8372727 3.96409091,10.71 L0.957272727,10.71 L0.957272727,13.0418182 C2.43818182,15.9831818 5.48181818,18 9,18 L9,18 Z"
id="Shape"
fill="#34A853"
fill="#000"
/>
<path
d="M3.96409091,10.71 C3.78409091,10.17 3.68181818,9.59318182 3.68181818,9 C3.68181818,8.40681818 3.78409091,7.83 3.96409091,7.29 L3.96409091,4.95818182 L0.957272727,4.95818182 C0.347727273,6.17318182 0,7.54772727 0,9 C0,10.4522727 0.347727273,11.8268182 0.957272727,13.0418182 L3.96409091,10.71 L3.96409091,10.71 Z"
id="Shape"
fill="#FBBC05"
fill="#000"
/>
<path
d="M9,3.57954545 C10.3213636,3.57954545 11.5077273,4.03363636 12.4404545,4.92545455 L15.0218182,2.34409091 C13.4631818,0.891818182 11.4259091,0 9,0 C5.48181818,0 2.43818182,2.01681818 0.957272727,4.95818182 L3.96409091,7.29 C4.67181818,5.16272727 6.65590909,3.57954545 9,3.57954545 L9,3.57954545 Z"
id="Shape"
fill="#EA4335"
fill="#000"
/>
<path d="M0,0 L18,0 L18,18 L0,18 L0,0 Z" id="Shape" />
</g>

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -0,0 +1,7 @@
<svg width="52" height="52" viewBox="0 0 52 52" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="26" cy="26" r="26" fill="white"/>
<rect x="15" y="15" width="7" height="22" fill="#1A4049"/>
<rect x="30" y="15" width="7" height="22" fill="#1A4049"/>
<path d="M37 30L37 37L15 37L15 30L37 30Z" fill="#1A4049"/>
<rect x="30" y="30" width="7" height="7" fill="#817AFE"/>
</svg>

After

Width:  |  Height:  |  Size: 384 B

View File

@ -0,0 +1,21 @@
import { makeStyles } from '@material-ui/styles';
export const useStyles = makeStyles(theme => ({
container: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
margin: '1rem auto',
},
wing: {
width: '80px',
height: '3px',
backgroundColor: theme.palette.division.main,
borderRadius: theme.borders.radius.main,
},
text: {
textAlign: 'center',
display: 'block',
margin: '0 1rem',
},
}));

View File

@ -0,0 +1,22 @@
import { Typography } from '@material-ui/core';
import { useStyles } from './DividerText.styles';
interface IDividerTextProps {
text: string;
}
const DividerText = ({ text, ...rest }: IDividerTextProps) => {
const styles = useStyles();
return (
<div className={styles.container} {...rest}>
<span className={styles.wing} />
<Typography variant="body2" className={styles.text}>
{text}
</Typography>
<span className={styles.wing} />
</div>
);
};
export default DividerText;

View File

@ -1,12 +1,14 @@
interface IGradientProps {
from: string;
to: string;
style?: object;
}
const Gradient: React.FC<IGradientProps> = ({
children,
from,
to,
style,
...rest
}) => {
return (
@ -15,6 +17,8 @@ const Gradient: React.FC<IGradientProps> = ({
background: `linear-gradient(${from}, ${to})`,
height: '100%',
width: '100%',
position: 'relative',
...style,
}}
{...rest}
>

View File

@ -4,7 +4,7 @@ import { Button, TextField } from '@material-ui/core';
import styles from './DemoAuth.module.scss';
import logoIcon from '../../../assets/img/logo.png';
import { ReactComponent as Logo } from '../../../assets/img/logo.svg';
const DemoAuth = ({ demoLogin, history, authDetails }) => {
const [email, setEmail] = useState('');
@ -24,13 +24,14 @@ const DemoAuth = ({ demoLogin, history, authDetails }) => {
return (
<form onSubmit={handleSubmit} action={authDetails.path}>
<Logo className={styles.logo} />
<div className={styles.container}>
<img alt="Unleash Logo" src={logoIcon} width="70" height="70" />
<h2>Access the Unleash demo instance</h2>
<p>No further data or Credit Card required</p>
<div className={styles.form}>
<TextField
value={email}
className={styles.emailField}
onChange={handleChange}
inputProps={{ 'data-test': 'email-input-field' }}
size="small"
@ -40,7 +41,7 @@ const DemoAuth = ({ demoLogin, history, authDetails }) => {
required
type="email"
/>
&nbsp;&nbsp;
<Button
type="submit"
variant="contained"

View File

@ -1,11 +1,34 @@
.container {
width: 350px;
}
.container > * {
margin: 0.6rem 0;
}
.button {
min-width: 150px;
.emailField {
width: 100%;
}
.form {
margin: 4em 1em
.form > * {
margin: 0.6rem 0;
}
.logo {
margin: 0 auto;
display: block;
height: 100px;
width: 100px;
}
.button {
min-width: 150px;
margin: 1rem auto;
display: block;
}
@media (max-width: 500px) {
.container {
width: 100%;
}
}

View File

@ -1,14 +1,21 @@
import { makeStyles } from '@material-ui/styles';
import { makeStyles } from '@material-ui/core/styles';
export const useStyles = makeStyles({
container: {
maxWidth: '300px',
export const useStyles = makeStyles(theme => ({
forgottenPassword: {
width: '350px',
[theme.breakpoints.down('xs')]: {
width: '100%',
},
},
button: {
width: '150px',
margin: '1rem auto',
},
email: {
display: 'block',
margin: '0.5rem 0',
},
});
loginText: {
textAlign: 'center',
},
}));

View File

@ -2,10 +2,12 @@ import { Button, TextField, Typography } from '@material-ui/core';
import { AlertTitle, Alert } from '@material-ui/lab';
import classnames from 'classnames';
import { SyntheticEvent, useState } from 'react';
import { Link } from 'react-router-dom';
import { useCommonStyles } from '../../../common.styles';
import useLoading from '../../../hooks/useLoading';
import { formatApiPath } from '../../../utils/format-path';
import ConditionallyRender from '../../common/ConditionallyRender';
import DividerText from '../../common/DividerText/DividerText';
import StandaloneLayout from '../common/StandaloneLayout/StandaloneLayout';
import { useStyles } from './ForgottenPassword.styles';
@ -41,7 +43,8 @@ const ForgottenPassword = () => {
<div
className={classnames(
commonStyles.contentSpacingY,
commonStyles.flexColumn
commonStyles.flexColumn,
styles.forgottenPassword
)}
ref={ref}
>
@ -105,6 +108,18 @@ const ForgottenPassword = () => {
elseShow={<span>Try again</span>}
/>
</Button>
<DividerText text="Or log in" />
<Button
type="submit"
data-loading
variant="outlined"
className={styles.button}
disabled={loading}
component={Link}
to="/login"
>
Log in
</Button>
</form>
</div>
</StandaloneLayout>

View File

@ -5,11 +5,12 @@ import { Button, Grid, TextField, Typography } from '@material-ui/core';
import { useHistory } from 'react-router';
import { useCommonStyles } from '../../../common.styles';
import { useStyles } from './HostedAuth.styles';
import { Link } from 'react-router-dom';
import useQueryParams from '../../../hooks/useQueryParams';
import AuthOptions from '../common/AuthOptions/AuthOptions';
import DividerText from '../../common/DividerText/DividerText';
import ConditionallyRender from '../../common/ConditionallyRender';
const PasswordAuth = ({ authDetails, passwordLogin }) => {
const HostedAuth = ({ authDetails, passwordLogin }) => {
const commonStyles = useCommonStyles();
const styles = useStyles();
const history = useHistory();
@ -67,12 +68,17 @@ const PasswordAuth = ({ authDetails, passwordLogin }) => {
const { options = [] } = authDetails;
return (
<div>
<br />
<div>
<>
<ConditionallyRender
condition={options.length > 0}
show={
<>
<AuthOptions options={options} />
</div>
<p className={styles.fancyLine}>or</p>
<DividerText text="or signin with username" />
</>
}
/>
<form onSubmit={handleSubmit} action={authDetails.path}>
<Typography variant="subtitle2" className={styles.apiError}>
{apiError}
@ -105,43 +111,26 @@ const PasswordAuth = ({ authDetails, passwordLogin }) => {
variant="outlined"
size="small"
/>
<Grid container spacing={3}>
<Grid item xs={6}>
<Grid container>
<Button
variant="contained"
color="primary"
type="submit"
style={{ maxWidth: '150px' }}
className={styles.button}
>
Sign in
</Button>
</Grid>
<Grid item xs={6}>
<Link to="/forgotten-password">
<Typography variant="body2" align="right">
Forgot your password?
</Typography>
</Link>
</Grid>
</Grid>
<br />
<br />
<Typography variant="body2" align="center">
Don't have an account? <br />{' '}
<a href="https://www.unleash-hosted.com/pricing">
Sign up
</a>
</Typography>
</div>
</form>
</div>
</>
);
};
PasswordAuth.propTypes = {
HostedAuth.propTypes = {
authDetails: PropTypes.object.isRequired,
passwordLogin: PropTypes.func.isRequired,
history: PropTypes.object.isRequired,
};
export default PasswordAuth;
export default HostedAuth;

View File

@ -15,26 +15,10 @@ export const useStyles = makeStyles(theme => ({
apiError: {
color: theme.palette.error.main,
},
fancyLine: {
display: 'flex',
width: '100%',
margin: '10px 0',
justifyContent: 'center',
alignItems: 'center',
button: {
width: '150px',
margin: '1rem auto 0 auto',
display: 'block',
textAlign: 'center',
color: 'gray',
'&::before': {
content: '""',
borderTop: '1px solid silver',
margin: '0 20px 0 0',
flex: '1 0 20px',
},
'&::after': {
content: '""',
borderTop: '1px solid silver',
margin: '0 20px 0 0',
flex: '1 0 20px',
}
}
}));

View File

@ -7,6 +7,7 @@ import { useStyles } from './Login.styles';
import useQueryParams from '../../../hooks/useQueryParams';
import ResetPasswordSuccess from '../common/ResetPasswordSuccess/ResetPasswordSuccess';
import StandaloneLayout from '../common/StandaloneLayout/StandaloneLayout';
import { DEMO_TYPE } from '../../../constants/authTypes';
const Login = ({ history, user, fetchUser }) => {
const styles = useStyles();
@ -28,16 +29,22 @@ const Login = ({ history, user, fetchUser }) => {
return (
<StandaloneLayout>
<div>
<h2 className={styles.title}>Login</h2>
<div className={styles.loginFormContainer}>
<ConditionallyRender
condition={user?.authDetails?.type !== DEMO_TYPE}
show={
<h2 className={styles.title}>
Login to continue the great work
</h2>
}
/>
<ConditionallyRender
condition={resetPassword}
show={<ResetPasswordSuccess />}
/>
<div className={styles.loginFormContainer}>
<AuthenticationContainer history={history} />
</div>
</div>
</StandaloneLayout>
);
};

View File

@ -1,6 +1,13 @@
import { makeStyles } from '@material-ui/styles';
export const useStyles = makeStyles(theme => ({
login: {
width: '350px',
maxWidth: '350px',
[theme.breakpoints.down('xs')]: {
width: '100%',
},
},
loginContainer: {
minHeight: '100vh',
width: '100%',
@ -25,7 +32,7 @@ export const useStyles = makeStyles(theme => ({
},
title: {
fontSize: theme.fontSizes.mainHeader,
marginBottom: '0.5rem',
marginBottom: '1rem',
display: 'flex',
alignItems: 'center',
},
@ -38,7 +45,8 @@ export const useStyles = makeStyles(theme => ({
fontSize: '1.25rem',
},
loginFormContainer: {
maxWidth: '500px',
display: 'flex',
flexDirection: 'column',
},
imageContainer: {
display: 'flex',

View File

@ -1,6 +1,22 @@
import { makeStyles } from '@material-ui/core/styles';
export const useStyles = makeStyles(theme => ({
newUser: {
width: '350px',
[theme.breakpoints.down('xs')]: {
width: '100%',
},
},
title: {
fontSize: theme.fontSizes.mainHeader,
marginBottom: '1.25rem',
display: 'flex',
alignItems: 'center',
},
inviteText: {
marginBottom: '1rem',
textAlign: 'center',
},
container: {
display: 'flex',
},
@ -9,7 +25,6 @@ export const useStyles = makeStyles(theme => ({
},
innerContainer: {
width: '60%',
minHeight: '100vh',
padding: '4rem 3rem',
},
buttonContainer: {
@ -20,14 +35,14 @@ export const useStyles = makeStyles(theme => ({
marginRight: '8px',
},
subtitle: {
marginBottom: '0.5rem',
fontSize: '1.1rem',
margin: '0.5rem 0',
},
passwordHeader: {
marginTop: '2rem',
},
emailField: {
minWidth: '300px',
width: '100%',
[theme.breakpoints.down('xs')]: {
minWidth: '100%',
},

View File

@ -5,13 +5,13 @@ import StandaloneBanner from '../StandaloneBanner/StandaloneBanner';
import ResetPasswordDetails from '../common/ResetPasswordDetails/ResetPasswordDetails';
import { useStyles } from './NewUser.styles';
import { useCommonStyles } from '../../../common.styles';
import useResetPassword from '../../../hooks/useResetPassword';
import StandaloneLayout from '../common/StandaloneLayout/StandaloneLayout';
import ConditionallyRender from '../../common/ConditionallyRender';
import InvalidToken from '../common/InvalidToken/InvalidToken';
import { IAuthStatus } from '../../../interfaces/user';
import AuthOptions from '../common/AuthOptions/AuthOptions';
import DividerText from '../../common/DividerText/DividerText';
interface INewUserProps {
user: IAuthStatus;
@ -21,26 +21,15 @@ const NewUser = ({ user }: INewUserProps) => {
const { token, data, loading, setLoading, invalidToken } =
useResetPassword();
const ref = useLoading(loading);
const commonStyles = useCommonStyles();
const styles = useStyles();
return (
<div ref={ref}>
<StandaloneLayout
showMenu={false}
BannerComponent={
<StandaloneBanner showStars title={'Welcome to Unleash'}>
<ConditionallyRender
condition={data?.createdBy}
show={
<Typography variant="body1">
You have been invited by {data?.createdBy}
</Typography>
}
/>
</StandaloneBanner>
}
BannerComponent={<StandaloneBanner title={'Unleash'} />}
>
<div className={styles.newUser}>
<ConditionallyRender
condition={invalidToken}
show={<InvalidToken />}
@ -49,13 +38,33 @@ const NewUser = ({ user }: INewUserProps) => {
token={token}
setLoading={setLoading}
>
<h2 className={styles.title}>
Enter your personal details and start your
journey
</h2>
<ConditionallyRender
condition={data?.createdBy}
show={
<Typography
variant="body1"
data-loading
className={styles.inviteText}
>
{data?.createdBy}
<br></br> has invited you to join
Unleash.
</Typography>
}
/>
<Typography
data-loading
variant="subtitle1"
variant="body1"
className={styles.subtitle}
>
Your username is
</Typography>
<TextField
data-loading
value={data?.email || ''}
@ -65,50 +74,28 @@ const NewUser = ({ user }: INewUserProps) => {
disabled
/>
<div className={styles.roleContainer}>
<Typography
data-loading
variant="subtitle1"
className={styles.subtitle}
>
In Unleash your role is:{' '}
<i>{data?.role?.name}</i>
</Typography>
<Typography variant="body1" data-loading>
{data?.role?.description}
</Typography>
<div
className={commonStyles.largeDivider}
data-loading
/>
<ConditionallyRender
condition={
user?.authDetails?.options?.length > 0
user?.authDetails?.options?.length >
0
}
show={
<>
<Typography data-loading>
Login with 3rd party providers
</Typography>
<DividerText
text="sign in with"
data-loading
/>
<AuthOptions
options={
user?.authDetails?.options
user?.authDetails
?.options
}
/>
<div
className={
commonStyles.largeDivider
}
<DividerText
text="or set a new password for your account"
data-loading
/>
<Typography
className={
styles.passwordHeader
}
data-loading
>
OR set a new password for your
account
</Typography>
</>
}
elseShow={
@ -124,6 +111,7 @@ const NewUser = ({ user }: INewUserProps) => {
</ResetPasswordDetails>
}
/>
</div>
</StandaloneLayout>
</div>
);

View File

@ -1,21 +1,19 @@
import React, { useState } from 'react';
import { useState } from 'react';
import classnames from 'classnames';
import PropTypes from 'prop-types';
import { Button, TextField, Typography, IconButton } from '@material-ui/core';
import LockRounded from '@material-ui/icons/LockRounded';
import { Button, TextField, Typography } from '@material-ui/core';
import ConditionallyRender from '../../common/ConditionallyRender';
import { useHistory } from 'react-router';
import { useCommonStyles } from '../../../common.styles';
import { useStyles } from './PasswordAuth.styles';
import { Link } from 'react-router-dom';
import useQueryParams from '../../../hooks/useQueryParams';
import { GoogleSvg } from '../HostedAuth/Icons';
import AuthOptions from '../common/AuthOptions/AuthOptions';
import DividerText from '../../common/DividerText/DividerText';
const PasswordAuth = ({ authDetails, passwordLogin }) => {
const commonStyles = useCommonStyles();
const styles = useStyles();
const history = useHistory();
const [showFields, setShowFields] = useState(false);
const params = useQueryParams();
const [username, setUsername] = useState(params.get('email') || '');
const [password, setPassword] = useState('');
@ -24,11 +22,6 @@ const PasswordAuth = ({ authDetails, passwordLogin }) => {
passwordError: '',
});
const onShowOptions = e => {
e.preventDefault();
setShowFields(true);
};
const handleSubmit = async evt => {
evt.preventDefault();
@ -110,17 +103,11 @@ const PasswordAuth = ({ authDetails, passwordLogin }) => {
autoComplete="true"
size="small"
/>
<Link to="/forgotten-password">
<Typography variant="body2">
Forgot your password?
</Typography>
</Link>
<Button
variant="contained"
color="primary"
type="submit"
style={{ maxWidth: '150px' }}
style={{ width: '150px', margin: '1rem auto' }}
>
Sign in
</Button>
@ -130,44 +117,23 @@ const PasswordAuth = ({ authDetails, passwordLogin }) => {
};
const renderWithOptions = options => (
<div>
{options.map(o => (
<div
key={o.type}
className={classnames(
styles.contentContainer,
commonStyles.contentSpacingY
)}
>
<Button color="primary" variant="outlined" href={o.path} startIcon={o.type === 'google' ? <GoogleSvg /> : <LockRounded />}>
{o.message}
</Button>
</div>
))}
<ConditionallyRender
condition={showFields}
show={renderLoginForm()}
elseShow={
<IconButton size="small" onClick={onShowOptions}>
Show more options
</IconButton>
}
/>
</div>
<>
<AuthOptions options={options} />
<DividerText text="Or signin with username" />
{renderLoginForm()}
</>
);
const { options = [] } = authDetails;
return (
<div>
<Typography variant="subtitle1">{authDetails.message}</Typography>
<>
<ConditionallyRender
condition={options.length > 0}
show={renderWithOptions(options)}
elseShow={renderLoginForm()}
/>
</div>
</>
);
};

View File

@ -1,7 +1,20 @@
.container {
width: 350px;
}
.container > * {
margin: 0.6rem 0;
width: 100%;
}
.button {
min-width: 150px;
margin: 0 auto;
display: block;
}
@media (max-width: 600px) {
.container {
width: 100%;
}
}

View File

@ -3,14 +3,22 @@ import { makeStyles } from '@material-ui/core/styles';
export const useStyles = makeStyles(theme => ({
title: {
color: '#fff',
fontSize: '1.2rem',
fontWeight: 'bold',
marginBottom: '1rem',
fontSize: '2rem',
marginTop: '5rem',
[theme.breakpoints.down('sm')]: {
textAlign: 'left',
fontSize: '1.75rem',
marginTop: 0,
},
},
container: {
padding: '4rem 2rem',
padding: '6rem 4rem',
color: '#fff',
position: 'relative',
borderTopLeftRadius: '3px',
borderBottomLeftRadius: '3px',
textAlign: 'right',
[theme.breakpoints.down('sm')]: {
padding: '3rem 2rem',
},
@ -18,9 +26,22 @@ export const useStyles = makeStyles(theme => ({
padding: '3rem 1rem',
},
},
bannerSubtitle: {
[theme.breakpoints.down('sm')]: {
maxWidth: '300px',
fontSize: '1.75rem',
textAlign: 'left',
},
[theme.breakpoints.down('xs')]: {
display: 'none',
},
fontSize: '2rem',
fontWeight: '300',
},
switchesContainer: {
position: 'absolute',
bottom: '40px',
bottom: '15px',
left: '-50px',
display: 'flex',
flexDirection: 'column',
[theme.breakpoints.down('sm')]: {
@ -28,46 +49,6 @@ export const useStyles = makeStyles(theme => ({
},
},
switchIcon: {
height: '180px',
},
bottomStar: {
position: 'absolute',
bottom: '-54px',
left: '100px',
[theme.breakpoints.down('sm')]: {
display: 'none',
},
},
bottomRightStar: {
position: 'absolute',
bottom: '-100px',
left: '200px',
[theme.breakpoints.down('sm')]: {
display: 'none',
},
},
midRightStar: {
position: 'absolute',
bottom: '-80px',
left: '300px',
[theme.breakpoints.down('sm')]: {
display: 'none',
},
},
midLeftStar: {
position: 'absolute',
top: '10px',
left: '150px',
[theme.breakpoints.down('sm')]: {
display: 'none',
},
},
midLeftStarTwo: {
position: 'absolute',
top: '25px',
left: '350px',
[theme.breakpoints.down('sm')]: {
display: 'none',
},
height: '100px',
},
}));

View File

@ -2,45 +2,35 @@ import { FC } from 'react';
import { Typography, useTheme } from '@material-ui/core';
import Gradient from '../../common/Gradient/Gradient';
import { ReactComponent as StarIcon } from '../../../assets/icons/star.svg';
import { ReactComponent as RightToggleIcon } from '../../../assets/icons/toggleRight.svg';
import { ReactComponent as LeftToggleIcon } from '../../../assets/icons/toggleLeft.svg';
import { useStyles } from './StandaloneBanner.styles';
import ConditionallyRender from '../../common/ConditionallyRender';
interface IStandaloneBannerProps {
showStars?: boolean;
title: string;
}
const StandaloneBanner: FC<IStandaloneBannerProps> = ({
showStars = false,
title,
children,
}) => {
const StandaloneBanner: FC<IStandaloneBannerProps> = ({ title, children }) => {
const theme = useTheme();
const styles = useStyles();
return (
<Gradient from={theme.palette.primary.main} to={'#173341'}>
<Gradient
from={theme.palette.primary.main}
to={theme.palette.login.gradient.bottom}
style={{
borderBottomLeftRadius: '3px',
borderTopLeftRadius: '3px',
overflow: 'hidden',
}}
>
<div className={styles.container}>
<Typography variant="h1" className={styles.title}>
{title}
</Typography>
{children}
<ConditionallyRender
condition={showStars}
show={
<>
<StarIcon className={styles.midLeftStarTwo} />
<StarIcon className={styles.midLeftStar} />
<StarIcon className={styles.midRightStar} />
<StarIcon className={styles.bottomRightStar} />
<StarIcon className={styles.bottomStar} />
</>
}
/>
<Typography className={styles.bannerSubtitle}>
Committed to creating new ways of developing software
</Typography>
</div>
<div className={styles.switchesContainer}>

View File

@ -6,10 +6,13 @@ import PasswordAuth from './PasswordAuth/PasswordAuth';
import HostedAuth from './HostedAuth/HostedAuth';
import DemoAuth from './DemoAuth';
const SIMPLE_TYPE = 'unsecure';
const DEMO_TYPE = 'demo';
const PASSWORD_TYPE = 'password';
const HOSTED_TYPE = 'enterprise-hosted';
import {
SIMPLE_TYPE,
DEMO_TYPE,
PASSWORD_TYPE,
HOSTED_TYPE,
} from '../../constants/authTypes';
import SecondaryLoginActions from './common/SecondaryLoginActions/SecondaryLoginActions';
class AuthComponent extends React.Component {
static propTypes = {
@ -22,16 +25,20 @@ class AuthComponent extends React.Component {
render() {
const authDetails = this.props.user.authDetails;
if (!authDetails) return null;
let content;
if (authDetails.type === PASSWORD_TYPE) {
content = (
<>
<PasswordAuth
passwordLogin={this.props.passwordLogin}
authDetails={authDetails}
history={this.props.history}
/>
<SecondaryLoginActions />
</>
);
} else if (authDetails.type === SIMPLE_TYPE) {
content = (
@ -51,18 +58,21 @@ class AuthComponent extends React.Component {
);
} else if (authDetails.type === HOSTED_TYPE) {
content = (
<>
<HostedAuth
passwordLogin={this.props.passwordLogin}
authDetails={authDetails}
history={this.props.history}
/>
<SecondaryLoginActions />
</>
);
} else {
content = (
<AuthenticationCustomComponent authDetails={authDetails} />
);
}
return <div>{content}</div>;
return <>{content}</>;
}
}

View File

@ -13,8 +13,14 @@ class AuthenticationCustomComponent extends React.Component {
<div>
<p>{authDetails.message}</p>
<CardActions style={{ textAlign: 'center' }}>
<a href={authDetails.path}>
<Button>Sign In</Button>
<a href={authDetails.path} style={{ width: '100%' }}>
<Button
variant="contained"
color="primary"
style={{ width: '150px', margin: '0 auto' }}
>
Sign In
</Button>
</a>
</CardActions>
</div>

View File

@ -4,6 +4,7 @@ import { useCommonStyles } from '../../../../common.styles';
import { IAuthOptions } from '../../../../interfaces/user';
import { ReactComponent as GoogleSvg } from '../../../../assets/icons/google.svg';
import LockRounded from '@material-ui/icons/LockRounded';
import ConditionallyRender from '../../../common/ConditionallyRender';
interface IAuthOptionProps {
options?: IAuthOptions[];
@ -27,13 +28,27 @@ const AuthOptions = ({ options }: IAuthOptionProps) => {
variant="outlined"
href={o.path}
size="small"
style={{ maxWidth: '300px' }}
style={{ height: '40px', color: '#000' }}
startIcon={
o.type === 'google' ? (
<GoogleSvg />
) : (
<LockRounded />
)
<ConditionallyRender
condition={o.type === 'google'}
show={
<GoogleSvg
style={{
height: '35px',
width: '35px',
}}
/>
}
elseShow={
<LockRounded
style={{
height: '25px',
width: '25px',
}}
/>
}
/>
}
>
{o.message}

View File

@ -10,6 +10,8 @@ import { formatApiPath } from '../../../../../utils/format-path';
interface IPasswordCheckerProps {
password: string;
callback: Dispatch<SetStateAction<boolean>>;
style?: object;
hideOnCompletion?: boolean;
}
interface IErrorResponse {
@ -30,7 +32,11 @@ const UPPERCASE_ERROR =
const LOWERCASE_ERROR =
'The password must contain at least one lowercase letter.';
const PasswordChecker = ({ password, callback }: IPasswordCheckerProps) => {
const PasswordChecker = ({
password,
callback,
style = {},
}: IPasswordCheckerProps) => {
const styles = useStyles();
const [casingError, setCasingError] = useState(true);
const [numberError, setNumberError] = useState(true);
@ -142,7 +148,12 @@ const PasswordChecker = ({ password, callback }: IPasswordCheckerProps) => {
<HelpIcon className={styles.helpIcon} />
</Typography>
</Tooltip>
<div className={styles.container}>
<div
className={styles.container}
style={{
...style,
}}
>
<div className={styles.headerContainer}>
<div className={styles.checkContainer}>
<Typography variant="body2" data-loading>

View File

@ -4,9 +4,11 @@ export const useStyles = makeStyles(theme => ({
container: {
display: 'flex',
flexDirection: 'column',
maxWidth: '300px',
position: 'relative',
},
button: {
width: '150px',
margin: '1rem auto',
display: 'block',
},
}));

View File

@ -28,6 +28,7 @@ const ResetPasswordForm = ({ token, setLoading }: IResetPasswordProps) => {
const commonStyles = useCommonStyles();
const [apiError, setApiError] = useState(false);
const [password, setPassword] = useState('');
const [showPasswordChecker, setShowPasswordChecker] = useState(false);
const [confirmPassword, setConfirmPassword] = useState('');
const [matchingPasswords, setMatchingPasswords] = useState(false);
const [validOwaspPassword, setValidOwaspPassword] = useState(false);
@ -102,10 +103,6 @@ const ResetPasswordForm = ({ token, setLoading }: IResetPasswordProps) => {
styles.container
)}
>
<PasswordChecker
password={password}
callback={setValidOwaspPasswordMemo}
/>
<TextField
variant="outlined"
size="small"
@ -113,6 +110,7 @@ const ResetPasswordForm = ({ token, setLoading }: IResetPasswordProps) => {
placeholder="Password"
value={password || ''}
onChange={e => setPassword(e.target.value)}
onFocus={() => setShowPasswordChecker(true)}
autoComplete="password"
data-loading
/>
@ -126,6 +124,17 @@ const ResetPasswordForm = ({ token, setLoading }: IResetPasswordProps) => {
autoComplete="confirm-password"
data-loading
/>
<ConditionallyRender
condition={showPasswordChecker}
show={
<PasswordChecker
password={password}
callback={setValidOwaspPasswordMemo}
style={{ marginBottom: '1rem' }}
/>
}
/>
<PasswordMatcher
started={started}
matchingPasswords={matchingPasswords}

View File

@ -0,0 +1,18 @@
import { makeStyles } from '@material-ui/core/styles';
export const useStyles = makeStyles(theme => ({
container: {
margin: 'auto auto 0 auto',
width: '200px',
[theme.breakpoints.down('sm')]: {
marginTop: '1rem',
},
},
link: {
textDecoration: 'none',
fontWeight: 'bold',
color: theme.palette.primary.main,
textAlign: 'center',
},
text: { fontWeight: 'bold', marginBottom: '0.5rem' },
}));

View File

@ -0,0 +1,29 @@
import { Typography } from '@material-ui/core';
import { Link } from 'react-router-dom';
import { useStyles } from './SecondaryLoginActions.styles';
const SecondaryLoginActions = () => {
const styles = useStyles();
return (
<div className={styles.container}>
<Link to="/forgotten-password" className={styles.link}>
<Typography variant="body2" className={styles.text}>
Forgot password?
</Typography>
</Link>
<Typography variant="body2">
Don't have an account?{' '}
<a
href="https://www.getunleash.io/plans"
target="_blank"
rel="noopener noreferrer"
className={styles.link}
>
Sign up
</a>
</Typography>
</div>
);
};
export default SecondaryLoginActions;

View File

@ -2,28 +2,31 @@ import { makeStyles } from '@material-ui/core/styles';
export const useStyles = makeStyles(theme => ({
container: {
padding: '4rem',
background: '#3a5663',
display: 'flex',
[theme.breakpoints.down('sm')]: {
flexDirection: 'column',
},
overflow: 'hidden',
[theme.breakpoints.down('xs')]: {
padding: '0',
},
minHeight: '100vh',
},
leftContainer: {
width: '40%',
minHeight: '100vh',
borderRadius: '3px',
[theme.breakpoints.down('sm')]: {
width: '100%',
minHeight: 'auto',
},
},
bannerSubtitle: {
[theme.breakpoints.down('sm')]: {
maxWidth: '300px',
},
},
rightContainer: {
width: '60%',
minHeight: '100vh',
flex: '1',
borderTopRightRadius: '3px',
borderBottomRightRadius: '3px',
backgroundColor: '#fff',
position: 'relative',
[theme.breakpoints.down('sm')]: {
width: '100%',
@ -31,27 +34,16 @@ export const useStyles = makeStyles(theme => ({
minHeight: 'auto',
},
},
menu: {
position: 'absolute',
right: '20px',
top: '20px',
'& a': {
textDecoration: 'none',
color: '#000',
},
[theme.breakpoints.down('sm')]: {
'& a': {
color: '#fff',
},
},
},
title: {
fontWeight: 'bold',
fontSize: '1.2rem',
marginBottom: '1rem',
},
innerRightContainer: {
padding: '4rem 3rem',
display: 'flex',
justifyContent: 'center',
height: '100%',
padding: '6rem 3rem',
[theme.breakpoints.down('sm')]: {
padding: '2rem 2rem',
},

View File

@ -1,11 +1,7 @@
import { FC } from 'react';
import StandaloneBanner from '../../StandaloneBanner/StandaloneBanner';
import { Typography } from '@material-ui/core';
import { useStyles } from './StandaloneLayout.styles';
import ConditionallyRender from '../../../common/ConditionallyRender';
import { Link, useLocation } from 'react-router-dom';
interface IStandaloneLayout {
BannerComponent?: JSX.Element;
@ -14,39 +10,20 @@ interface IStandaloneLayout {
const StandaloneLayout: FC<IStandaloneLayout> = ({
children,
showMenu = true,
BannerComponent,
}) => {
const styles = useStyles();
const location = useLocation();
let banner = (
<StandaloneBanner title="Unleash">
<Typography variant="subtitle1" className={styles.bannerSubtitle}>
Committed to creating new ways of developing software.
</Typography>
</StandaloneBanner>
);
let banner = <StandaloneBanner title="Unleash" />;
if (BannerComponent) {
banner = BannerComponent;
}
const isLoginpage = location.pathname.includes('login');
return (
<div className={styles.container}>
<div className={styles.leftContainer}>{banner}</div>
<div className={styles.rightContainer}>
<ConditionallyRender
condition={showMenu && !isLoginpage}
show={
<div className={styles.menu}>
<Link to="/login">Login</Link>
</div>
}
/>
<div className={styles.innerRightContainer}>{children}</div>
</div>
</div>

View File

@ -0,0 +1,5 @@
/* AUTH TYPES */
export const SIMPLE_TYPE = 'unsecure';
export const DEMO_TYPE = 'demo';
export const PASSWORD_TYPE = 'password';
export const HOSTED_TYPE = 'enterprise-hosted';

View File

@ -1,10 +1,19 @@
import * as createPalette from '@material-ui/core/styles/createPalette';
declare module '@material-ui/core/styles/createPalette' {
interface PaletteOptions {
borders?: PaletteColorOptions;
login?: ILoginPaletteOptions;
}
interface Palette {
borders?: PaletteColor;
}
}
interface ILoginPaletteOptions {
gradient?: IGradientPaletteOptions;
main?: string;
}
interface IGradientPaletteOptions {
top: string;
bottom: string;
}