1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-06-27 01:19:00 +02:00

Makestyles 7-1 (#2813)

This commit is contained in:
sjaanus 2023-01-04 11:17:13 +02:00 committed by GitHub
parent 0af162a8e6
commit 45652f6bf9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 585 additions and 742 deletions

View File

@ -6,9 +6,9 @@ import { modalStyles } from 'component/admin/users/util';
import { Dialogue } from 'component/common/Dialogue/Dialogue'; import { Dialogue } from 'component/common/Dialogue/Dialogue';
import PasswordChecker, { import PasswordChecker, {
PASSWORD_FORMAT_MESSAGE, PASSWORD_FORMAT_MESSAGE,
} from 'component/user/common/ResetPasswordForm/PasswordChecker/PasswordChecker'; } from 'component/user/common/ResetPasswordForm/PasswordChecker';
import { useThemeStyles } from 'themes/themeStyles'; import { useThemeStyles } from 'themes/themeStyles';
import PasswordMatcher from 'component/user/common/ResetPasswordForm/PasswordMatcher/PasswordMatcher'; import PasswordMatcher from 'component/user/common/ResetPasswordForm/PasswordMatcher';
import { IUser } from 'interfaces/user'; import { IUser } from 'interfaces/user';
import useAdminUsersApi from 'hooks/api/actions/useAdminUsersApi/useAdminUsersApi'; import useAdminUsersApi from 'hooks/api/actions/useAdminUsersApi/useAdminUsersApi';
import { UserAvatar } from 'component/common/UserAvatar/UserAvatar'; import { UserAvatar } from 'component/common/UserAvatar/UserAvatar';

View File

@ -1,7 +1,7 @@
import SimpleAuth from '../SimpleAuth/SimpleAuth'; import SimpleAuth from '../SimpleAuth/SimpleAuth';
import { AuthenticationCustomComponent } from 'component/user/AuthenticationCustomComponent'; import { AuthenticationCustomComponent } from 'component/user/AuthenticationCustomComponent';
import PasswordAuth from '../PasswordAuth/PasswordAuth'; import PasswordAuth from '../PasswordAuth';
import HostedAuth from '../HostedAuth/HostedAuth'; import HostedAuth from '../HostedAuth';
import DemoAuth from '../DemoAuth/DemoAuth'; import DemoAuth from '../DemoAuth/DemoAuth';
import { import {
SIMPLE_TYPE, SIMPLE_TYPE,
@ -9,7 +9,7 @@ import {
PASSWORD_TYPE, PASSWORD_TYPE,
HOSTED_TYPE, HOSTED_TYPE,
} from 'constants/authTypes'; } from 'constants/authTypes';
import SecondaryLoginActions from '../common/SecondaryLoginActions/SecondaryLoginActions'; import SecondaryLoginActions from '../common/SecondaryLoginActions';
import useQueryParams from 'hooks/useQueryParams'; import useQueryParams from 'hooks/useQueryParams';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { Alert } from '@mui/material'; import { Alert } from '@mui/material';

View File

@ -1,21 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
forgottenPassword: {
width: '350px',
[theme.breakpoints.down('sm')]: {
width: '100%',
},
},
button: {
width: '150px',
margin: '1rem auto',
},
email: {
display: 'block',
margin: '0.5rem 0',
},
loginText: {
textAlign: 'center',
},
}));

View File

@ -1,24 +1,58 @@
import { Button, TextField, Typography } from '@mui/material'; import { Button, styled, TextField, Typography } from '@mui/material';
import { AlertTitle, Alert } from '@mui/material'; import { AlertTitle, Alert } from '@mui/material';
import classnames from 'classnames';
import { SyntheticEvent, useState } from 'react'; import { SyntheticEvent, useState } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { useThemeStyles } from 'themes/themeStyles';
import useLoading from 'hooks/useLoading'; import useLoading from 'hooks/useLoading';
import { FORGOTTEN_PASSWORD_FIELD } from 'utils/testIds'; import { FORGOTTEN_PASSWORD_FIELD } from 'utils/testIds';
import { formatApiPath } from 'utils/formatPath'; import { formatApiPath } from 'utils/formatPath';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import DividerText from 'component/common/DividerText/DividerText'; import DividerText from 'component/common/DividerText/DividerText';
import StandaloneLayout from '../common/StandaloneLayout/StandaloneLayout'; import StandaloneLayout from '../common/StandaloneLayout';
import { useStyles } from './ForgottenPassword.styles'; import {
contentSpacingY,
flexColumn,
textCenter,
title,
} from 'themes/themeStyles';
const StyledDiv = styled('div')(({ theme }) => ({
...contentSpacingY,
...flexColumn,
width: '350px',
[theme.breakpoints.down('sm')]: {
width: '100%',
},
}));
const StyledStrong = styled('strong')(({ theme }) => ({
display: 'block',
margin: theme.spacing(1, 0),
}));
const StyledButton = styled(Button)(({ theme }) => ({
width: '150px',
margin: theme.spacing(2, 'auto'),
}));
const StyledTitle = styled('h2')(({ theme }) => ({
...title(theme),
...textCenter,
}));
const StyledForm = styled('form')(({ theme }) => ({
...contentSpacingY(theme),
...flexColumn,
}));
const StyledTypography = styled(Typography)(({ theme }) => ({
...textCenter,
}));
const ForgottenPassword = () => { const ForgottenPassword = () => {
const [email, setEmail] = useState(''); const [email, setEmail] = useState('');
const [attempted, setAttempted] = useState(false); const [attempted, setAttempted] = useState(false);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [attemptedEmail, setAttemptedEmail] = useState(''); const [attemptedEmail, setAttemptedEmail] = useState('');
const { classes: themeStyles } = useThemeStyles();
const { classes: styles } = useStyles();
const ref = useLoading(loading); const ref = useLoading(loading);
const onClick = async (e: SyntheticEvent) => { const onClick = async (e: SyntheticEvent) => {
@ -41,32 +75,15 @@ const ForgottenPassword = () => {
return ( return (
<StandaloneLayout> <StandaloneLayout>
<div <StyledDiv ref={ref}>
className={classnames( <StyledTitle data-loading>Forgotten password</StyledTitle>
themeStyles.contentSpacingY,
themeStyles.flexColumn,
styles.forgottenPassword
)}
ref={ref}
>
<h2
className={classnames(
themeStyles.title,
themeStyles.textCenter
)}
data-loading
>
Forgotten password
</h2>
<ConditionallyRender <ConditionallyRender
condition={attempted} condition={attempted}
show={ show={
<Alert severity="success" data-loading> <Alert severity="success" data-loading>
<AlertTitle>Attempted to send email</AlertTitle> <AlertTitle>Attempted to send email</AlertTitle>
We've attempted to send a reset password email to: We've attempted to send a reset password email to:
<strong className={styles.email}> <StyledStrong>{attemptedEmail}</StyledStrong>
{attemptedEmail}
</strong>
If you did not receive an email, please verify that If you did not receive an email, please verify that
you typed in the correct email, and contact your you typed in the correct email, and contact your
administrator to make sure that you are in the administrator to make sure that you are in the
@ -74,21 +91,11 @@ const ForgottenPassword = () => {
</Alert> </Alert>
} }
/> />
<form <StyledForm onSubmit={onClick}>
onSubmit={onClick} <StyledTypography variant="body1" data-loading>
className={classnames(
themeStyles.contentSpacingY,
themeStyles.flexColumn
)}
>
<Typography
variant="body1"
data-loading
className={themeStyles.textCenter}
>
Please provide your email address. If it exists in the Please provide your email address. If it exists in the
system we'll send a new reset link. system we'll send a new reset link.
</Typography> </StyledTypography>
<TextField <TextField
variant="outlined" variant="outlined"
size="small" size="small"
@ -101,12 +108,11 @@ const ForgottenPassword = () => {
setEmail(e.target.value); setEmail(e.target.value);
}} }}
/> />
<Button <StyledButton
variant="contained" variant="contained"
type="submit" type="submit"
data-loading data-loading
color="primary" color="primary"
className={styles.button}
disabled={loading} disabled={loading}
> >
<ConditionallyRender <ConditionallyRender
@ -114,21 +120,24 @@ const ForgottenPassword = () => {
show={<span>Submit</span>} show={<span>Submit</span>}
elseShow={<span>Try again</span>} elseShow={<span>Try again</span>}
/> />
</Button> </StyledButton>
<DividerText text="Or log in" /> <DividerText text="Or log in" />
<Button <Button
type="submit" type="submit"
data-loading data-loading
variant="outlined" variant="outlined"
className={styles.button}
disabled={loading} disabled={loading}
component={Link} component={Link}
to="/login" to="/login"
sx={theme => ({
width: '150px',
margin: theme.spacing(2, 'auto'),
})}
> >
Log in Log in
</Button> </Button>
</form> </StyledForm>
</div> </StyledDiv>
</StandaloneLayout> </StandaloneLayout>
); );
}; };

View File

@ -1,11 +1,8 @@
import { FormEventHandler, useState, VFC } from 'react'; import { FormEventHandler, useState, VFC } from 'react';
import classnames from 'classnames'; import { Button, Grid, styled, TextField, Typography } from '@mui/material';
import { Button, Grid, TextField, Typography } from '@mui/material';
import { useNavigate } from 'react-router'; import { useNavigate } from 'react-router';
import { useThemeStyles } from 'themes/themeStyles';
import { useStyles } from './HostedAuth.styles';
import useQueryParams from 'hooks/useQueryParams'; import useQueryParams from 'hooks/useQueryParams';
import AuthOptions from '../common/AuthOptions/AuthOptions'; import AuthOptions from './common/AuthOptions/AuthOptions';
import DividerText from 'component/common/DividerText/DividerText'; import DividerText from 'component/common/DividerText/DividerText';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import PasswordField from 'component/common/PasswordField/PasswordField'; import PasswordField from 'component/common/PasswordField/PasswordField';
@ -14,15 +11,31 @@ import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser';
import { LOGIN_BUTTON, LOGIN_EMAIL_ID, LOGIN_PASSWORD_ID } from 'utils/testIds'; import { LOGIN_BUTTON, LOGIN_EMAIL_ID, LOGIN_PASSWORD_ID } from 'utils/testIds';
import { IAuthEndpointDetailsResponse } from 'hooks/api/getters/useAuth/useAuthEndpoint'; import { IAuthEndpointDetailsResponse } from 'hooks/api/getters/useAuth/useAuthEndpoint';
import { BadRequestError, NotFoundError } from 'utils/apiUtils'; import { BadRequestError, NotFoundError } from 'utils/apiUtils';
import { contentSpacingY } from 'themes/themeStyles';
interface IHostedAuthProps { interface IHostedAuthProps {
authDetails: IAuthEndpointDetailsResponse; authDetails: IAuthEndpointDetailsResponse;
redirect: string; redirect: string;
} }
const StyledTypography = styled(Typography)(({ theme }) => ({
color: theme.palette.error.main,
}));
const StyledDiv = styled('div')(({ theme }) => ({
...contentSpacingY(theme),
display: 'flex',
flexDirection: 'column',
}));
const StyledButton = styled(Button)(({ theme }) => ({
width: '150px',
margin: theme.spacing(2, 'auto', 0, 'auto'),
display: 'block',
textAlign: 'center',
}));
const HostedAuth: VFC<IHostedAuthProps> = ({ authDetails, redirect }) => { const HostedAuth: VFC<IHostedAuthProps> = ({ authDetails, redirect }) => {
const { classes: themeStyles } = useThemeStyles();
const { classes: styles } = useStyles();
const { refetchUser } = useAuthUser(); const { refetchUser } = useAuthUser();
const navigate = useNavigate(); const navigate = useNavigate();
const params = useQueryParams(); const params = useQueryParams();
@ -97,18 +110,10 @@ const HostedAuth: VFC<IHostedAuthProps> = ({ authDetails, redirect }) => {
condition={!authDetails.defaultHidden} condition={!authDetails.defaultHidden}
show={ show={
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<Typography <StyledTypography variant="subtitle2">
variant="subtitle2"
className={styles.apiError}
>
{apiError} {apiError}
</Typography> </StyledTypography>
<div <StyledDiv>
className={classnames(
styles.contentContainer,
themeStyles.contentSpacingY
)}
>
<TextField <TextField
label="Username or email" label="Username or email"
name="username" name="username"
@ -134,17 +139,16 @@ const HostedAuth: VFC<IHostedAuthProps> = ({ authDetails, redirect }) => {
data-testid={LOGIN_PASSWORD_ID} data-testid={LOGIN_PASSWORD_ID}
/> />
<Grid container> <Grid container>
<Button <StyledButton
variant="contained" variant="contained"
color="primary" color="primary"
type="submit" type="submit"
className={styles.button}
data-testid={LOGIN_BUTTON} data-testid={LOGIN_BUTTON}
> >
Sign in Sign in
</Button> </StyledButton>
</Grid> </Grid>
</div> </StyledDiv>
</form> </form>
} }
/> />

View File

@ -1,24 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
loginContainer: {
minWidth: '350px',
[theme.breakpoints.down('sm')]: {
width: '100%',
minWidth: 'auto',
},
},
contentContainer: {
display: 'flex',
flexDirection: 'column',
},
apiError: {
color: theme.palette.error.main,
},
button: {
width: '150px',
margin: '1rem auto 0 auto',
display: 'block',
textAlign: 'center',
},
}));

View File

@ -1,51 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
login: {
width: '350px',
maxWidth: '350px',
[theme.breakpoints.down('sm')]: {
width: '100%',
},
},
loginContainer: {
minHeight: '100vh',
width: '100%',
},
container: {
display: 'flex',
height: '100%',
flexWrap: 'wrap',
},
contentContainer: {
width: '50%',
padding: '4rem 3rem',
minHeight: '100vh',
[theme.breakpoints.down('md')]: {
width: '100%',
minHeight: 'auto',
},
},
title: {
fontSize: theme.fontSizes.mainHeader,
marginBottom: '1rem',
textAlign: 'center',
},
logo: {
marginRight: '10px',
width: '40px',
height: '30px',
},
subTitle: {
fontSize: '1.25rem',
},
loginFormContainer: {
display: 'flex',
flexDirection: 'column',
},
imageContainer: {
display: 'flex',
justifyContent: 'center',
marginTop: '8rem',
},
}));

View File

@ -1,17 +1,26 @@
import { Navigate } from 'react-router-dom'; import { Navigate } from 'react-router-dom';
import { Alert, AlertTitle } from '@mui/material'; import { Alert, AlertTitle, styled } from '@mui/material';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { useStyles } from 'component/user/Login/Login.styles';
import useQueryParams from 'hooks/useQueryParams'; import useQueryParams from 'hooks/useQueryParams';
import StandaloneLayout from '../common/StandaloneLayout/StandaloneLayout'; import StandaloneLayout from '../common/StandaloneLayout';
import { DEMO_TYPE } from 'constants/authTypes'; import { DEMO_TYPE } from 'constants/authTypes';
import Authentication from '../Authentication/Authentication'; import Authentication from '../Authentication/Authentication';
import { useAuthDetails } from 'hooks/api/getters/useAuth/useAuthDetails'; import { useAuthDetails } from 'hooks/api/getters/useAuth/useAuthDetails';
import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser'; import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser';
import { parseRedirectParam } from 'component/user/Login/parseRedirectParam'; import { parseRedirectParam } from 'component/user/Login/parseRedirectParam';
const StyledDiv = styled('div')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
}));
const StyledHeader = styled('h2')(({ theme }) => ({
fontSize: theme.fontSizes.mainHeader,
marginBottom: theme.spacing(2),
textAlign: 'center',
}));
const Login = () => { const Login = () => {
const { classes: styles } = useStyles();
const { authDetails } = useAuthDetails(); const { authDetails } = useAuthDetails();
const { user } = useAuthUser(); const { user } = useAuthUser();
const query = useQueryParams(); const query = useQueryParams();
@ -25,7 +34,7 @@ const Login = () => {
return ( return (
<StandaloneLayout> <StandaloneLayout>
<div className={styles.loginFormContainer}> <StyledDiv>
<ConditionallyRender <ConditionallyRender
condition={resetPassword} condition={resetPassword}
show={ show={
@ -47,13 +56,13 @@ const Login = () => {
<ConditionallyRender <ConditionallyRender
condition={authDetails?.type !== DEMO_TYPE} condition={authDetails?.type !== DEMO_TYPE}
show={ show={
<h2 className={styles.title}> <StyledHeader>
Log in to continue the great work Log in to continue the great work
</h2> </StyledHeader>
} }
/> />
<Authentication redirect={redirect} /> <Authentication redirect={redirect} />
</div> </StyledDiv>
</StandaloneLayout> </StandaloneLayout>
); );
}; };

View File

@ -1,7 +1,7 @@
import { FC } from 'react'; import { FC } from 'react';
import { Box, Typography } from '@mui/material'; import { Box, Typography } from '@mui/material';
import StandaloneLayout from 'component/user/common/StandaloneLayout/StandaloneLayout'; import StandaloneLayout from 'component/user/common/StandaloneLayout';
import StandaloneBanner from 'component/user/StandaloneBanner/StandaloneBanner'; import StandaloneBanner from 'component/user/StandaloneBanner';
import useLoading from 'hooks/useLoading'; import useLoading from 'hooks/useLoading';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';

View File

@ -1,12 +1,10 @@
import { FormEventHandler, useState, VFC } from 'react'; import { FormEventHandler, useState, VFC } from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import { Button, TextField } from '@mui/material'; import { Button, styled, TextField } from '@mui/material';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { useNavigate } from 'react-router'; import { useNavigate } from 'react-router';
import { useThemeStyles } from 'themes/themeStyles';
import { useStyles } from './PasswordAuth.styles';
import useQueryParams from 'hooks/useQueryParams'; import useQueryParams from 'hooks/useQueryParams';
import AuthOptions from '../common/AuthOptions/AuthOptions'; import AuthOptions from './common/AuthOptions/AuthOptions';
import DividerText from 'component/common/DividerText/DividerText'; import DividerText from 'component/common/DividerText/DividerText';
import { Alert } from '@mui/material'; import { Alert } from '@mui/material';
import { LOGIN_BUTTON, LOGIN_EMAIL_ID, LOGIN_PASSWORD_ID } from 'utils/testIds'; import { LOGIN_BUTTON, LOGIN_EMAIL_ID, LOGIN_PASSWORD_ID } from 'utils/testIds';
@ -19,15 +17,25 @@ import {
BadRequestError, BadRequestError,
NotFoundError, NotFoundError,
} from 'utils/apiUtils'; } from 'utils/apiUtils';
import { contentSpacingY } from 'themes/themeStyles';
interface IPasswordAuthProps { interface IPasswordAuthProps {
authDetails: IAuthEndpointDetailsResponse; authDetails: IAuthEndpointDetailsResponse;
redirect: string; redirect: string;
} }
const StyledAlert = styled(Alert)(({ theme }) => ({
color: theme.palette.error.main,
marginBottom: theme.spacing(1),
}));
const StyledDiv = styled('div')(({ theme }) => ({
...contentSpacingY(theme),
display: 'flex',
flexDirection: 'column',
}));
const PasswordAuth: VFC<IPasswordAuthProps> = ({ authDetails, redirect }) => { const PasswordAuth: VFC<IPasswordAuthProps> = ({ authDetails, redirect }) => {
const { classes: themeStyles } = useThemeStyles();
const { classes: styles } = useStyles();
const navigate = useNavigate(); const navigate = useNavigate();
const { refetchUser } = useAuthUser(); const { refetchUser } = useAuthUser();
const params = useQueryParams(); const params = useQueryParams();
@ -98,21 +106,13 @@ const PasswordAuth: VFC<IPasswordAuthProps> = ({ authDetails, redirect }) => {
<ConditionallyRender <ConditionallyRender
condition={Boolean(apiError)} condition={Boolean(apiError)}
show={ show={
<Alert <StyledAlert severity="error">
severity="error"
className={styles.apiError}
>
{apiError} {apiError}
</Alert> </StyledAlert>
} }
/> />
<div <StyledDiv>
className={classnames(
styles.contentContainer,
themeStyles.contentSpacingY
)}
>
<TextField <TextField
label="Username or email" label="Username or email"
name="username" name="username"
@ -148,7 +148,7 @@ const PasswordAuth: VFC<IPasswordAuthProps> = ({ authDetails, redirect }) => {
> >
Sign in Sign in
</Button> </Button>
</div> </StyledDiv>
</form> </form>
} }
/> />

View File

@ -1,19 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
loginContainer: {
minWidth: '350px',
[theme.breakpoints.down('sm')]: {
width: '100%',
minWidth: 'auto',
},
},
contentContainer: {
display: 'flex',
flexDirection: 'column',
},
apiError: {
color: theme.palette.error.main,
marginBottom: '0.5rem',
},
}));

View File

@ -3,8 +3,8 @@ import { PageContent } from 'component/common/PageContent/PageContent';
import PasswordField from 'component/common/PasswordField/PasswordField'; import PasswordField from 'component/common/PasswordField/PasswordField';
import PasswordChecker, { import PasswordChecker, {
PASSWORD_FORMAT_MESSAGE, PASSWORD_FORMAT_MESSAGE,
} from 'component/user/common/ResetPasswordForm/PasswordChecker/PasswordChecker'; } from 'component/user/common/ResetPasswordForm/PasswordChecker';
import PasswordMatcher from 'component/user/common/ResetPasswordForm/PasswordMatcher/PasswordMatcher'; import PasswordMatcher from 'component/user/common/ResetPasswordForm/PasswordMatcher';
import { usePasswordApi } from 'hooks/api/actions/usePasswordApi/usePasswordApi'; import { usePasswordApi } from 'hooks/api/actions/usePasswordApi/usePasswordApi';
import useToast from 'hooks/useToast'; import useToast from 'hooks/useToast';
import { SyntheticEvent, useState } from 'react'; import { SyntheticEvent, useState } from 'react';

View File

@ -1,20 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
resetPassword: {
width: '350px',
maxWidth: '350px',
[theme.breakpoints.down('sm')]: {
width: '100%',
},
},
container: {
display: 'flex',
},
innerContainer: { width: '40%', minHeight: '100vh' },
title: {
fontWeight: 'bold',
fontSize: '1.2rem',
marginBottom: '1rem',
},
}));

View File

@ -2,19 +2,31 @@ import { useState } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { OK } from 'constants/statusCodes'; import { OK } from 'constants/statusCodes';
import useLoading from 'hooks/useLoading'; import useLoading from 'hooks/useLoading';
import { useStyles } from './ResetPassword.styles'; import { styled, Typography } from '@mui/material';
import { Typography } from '@mui/material';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import InvalidToken from '../common/InvalidToken/InvalidToken'; import InvalidToken from '../common/InvalidToken/InvalidToken';
import useResetPassword from 'hooks/api/getters/useResetPassword/useResetPassword'; import useResetPassword from 'hooks/api/getters/useResetPassword/useResetPassword';
import StandaloneLayout from '../common/StandaloneLayout/StandaloneLayout'; import StandaloneLayout from '../common/StandaloneLayout';
import ResetPasswordForm from '../common/ResetPasswordForm/ResetPasswordForm'; import ResetPasswordForm from '../common/ResetPasswordForm/ResetPasswordForm';
import ResetPasswordError from '../common/ResetPasswordError/ResetPasswordError'; import ResetPasswordError from '../common/ResetPasswordError/ResetPasswordError';
import { useAuthResetPasswordApi } from 'hooks/api/actions/useAuthResetPasswordApi/useAuthResetPasswordApi'; import { useAuthResetPasswordApi } from 'hooks/api/actions/useAuthResetPasswordApi/useAuthResetPasswordApi';
import { useAuthDetails } from 'hooks/api/getters/useAuth/useAuthDetails'; import { useAuthDetails } from 'hooks/api/getters/useAuth/useAuthDetails';
const StyledDiv = styled('div')(({ theme }) => ({
width: '350px',
maxWidth: '350px',
[theme.breakpoints.down('sm')]: {
width: '100%',
},
}));
const StyledTypography = styled(Typography)(({ theme }) => ({
fontWeight: 'bold',
fontSize: theme.spacing(2.5),
marginBottom: theme.spacing(2),
}));
const ResetPassword = () => { const ResetPassword = () => {
const { classes: styles } = useStyles();
const { token, loading, isValidToken } = useResetPassword(); const { token, loading, isValidToken } = useResetPassword();
const { resetPassword, loading: actionLoading } = useAuthResetPasswordApi(); const { resetPassword, loading: actionLoading } = useAuthResetPasswordApi();
const { authDetails } = useAuthDetails(); const { authDetails } = useAuthDetails();
@ -40,19 +52,15 @@ const ResetPassword = () => {
return ( return (
<div ref={ref}> <div ref={ref}>
<StandaloneLayout> <StandaloneLayout>
<div className={styles.resetPassword}> <StyledDiv>
<ConditionallyRender <ConditionallyRender
condition={!isValidToken || passwordDisabled} condition={!isValidToken || passwordDisabled}
show={<InvalidToken />} show={<InvalidToken />}
elseShow={ elseShow={
<> <>
<Typography <StyledTypography variant="h2" data-loading>
variant="h2"
className={styles.title}
data-loading
>
Reset password Reset password
</Typography> </StyledTypography>
<ConditionallyRender <ConditionallyRender
condition={hasApiError} condition={hasApiError}
@ -62,7 +70,7 @@ const ResetPassword = () => {
</> </>
} }
/> />
</div> </StyledDiv>
</StandaloneLayout> </StandaloneLayout>
</div> </div>
); );

View File

@ -0,0 +1,110 @@
import { FC } from 'react';
import { Typography, useTheme, useMediaQuery, styled } from '@mui/material';
import Gradient from 'component/common/Gradient/Gradient';
import { ReactComponent as Logo } from 'assets/icons/logoWhiteBg.svg';
import { ReactComponent as LogoWithText } from 'assets/img/logoWhiteTransparentHorizontal.svg';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { Theme } from '@mui/material';
interface IStandaloneBannerProps {
title: string;
}
const StyledGradient = styled(Gradient)(({ theme }) => ({
display: 'flex',
justifyContent: 'center',
[theme.breakpoints.up('sm')]: {
borderBottomLeftRadius: '3px',
borderTopLeftRadius: '3px',
},
}));
const StyledContainer = styled('div')(({ theme }) => ({
padding: theme.spacing(12, 8),
color: '#fff',
position: 'relative',
borderTopLeftRadius: '3px',
borderBottomLeftRadius: '3px',
textAlign: 'right',
[theme.breakpoints.down('md')]: {
padding: theme.spacing(6, 4),
},
[theme.breakpoints.down('sm')]: {
padding: theme.spacing(6, 2),
},
}));
const StyledTitle = styled(Typography)(({ theme }) => ({
color: '#fff',
marginBottom: theme.spacing(2),
fontSize: theme.spacing(4),
marginTop: theme.spacing(10),
[theme.breakpoints.down('md')]: {
display: 'none',
},
}));
const StyledSubtitle = styled(Typography)(({ theme }) => ({
[theme.breakpoints.down('md')]: {
display: 'none',
},
[theme.breakpoints.down('sm')]: {
display: 'none',
},
fontSize: theme.spacing(4),
fontWeight: 'normal',
}));
const StyledLogoContainer = styled('div')(({ theme }) => ({
position: 'absolute',
[theme.breakpoints.up('md')]: {
bottom: '-50px',
left: '-50px',
display: 'flex',
flexDirection: 'column',
},
}));
const logoStyles = (theme: Theme) => ({
width: '200px',
[theme.breakpoints.up('md')]: {
width: '240px',
height: '200px',
},
});
const StyledLogoWithText = styled(LogoWithText)(({ theme }) => ({
...logoStyles(theme),
}));
const StyledLogo = styled(Logo)(({ theme }) => ({
...logoStyles(theme),
}));
const StandaloneBanner: FC<IStandaloneBannerProps> = ({ title }) => {
const theme = useTheme();
const smallScreen = useMediaQuery(theme.breakpoints.down('md'));
return (
<StyledGradient
from={theme.palette.standaloneBannerGradient.from}
to={theme.palette.standaloneBannerGradient.to}
>
<StyledContainer>
<StyledTitle variant="h1">{title}</StyledTitle>
<StyledSubtitle>
Committed to creating new ways of developing software
</StyledSubtitle>
</StyledContainer>
<StyledLogoContainer>
<ConditionallyRender
condition={smallScreen}
show={<StyledLogoWithText aria-label="Unleash logo" />}
elseShow={<StyledLogo aria-label="Unleash logo" />}
/>
</StyledLogoContainer>
</StyledGradient>
);
};
export default StandaloneBanner;

View File

@ -1,61 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
gradient: {
display: 'flex',
justifyContent: 'center',
[theme.breakpoints.up('sm')]: {
borderBottomLeftRadius: '3px',
borderTopLeftRadius: '3px',
},
},
title: {
color: '#fff',
marginBottom: '1rem',
fontSize: '2rem',
marginTop: '5rem',
[theme.breakpoints.down('md')]: {
display: 'none',
},
},
container: {
padding: '6rem 4rem',
color: '#fff',
position: 'relative',
borderTopLeftRadius: '3px',
borderBottomLeftRadius: '3px',
textAlign: 'right',
[theme.breakpoints.down('md')]: {
padding: '3rem 2rem',
},
[theme.breakpoints.down('sm')]: {
padding: '3rem 1rem',
},
},
bannerSubtitle: {
[theme.breakpoints.down('md')]: {
display: 'none',
},
[theme.breakpoints.down('sm')]: {
display: 'none',
},
fontSize: '2rem',
fontWeight: 'normal',
},
logoContainer: {
position: 'absolute',
[theme.breakpoints.up('md')]: {
bottom: '-50px',
left: '-50px',
display: 'flex',
flexDirection: 'column',
},
},
logo: {
width: '200px',
[theme.breakpoints.up('md')]: {
width: '240px',
height: '200px',
},
},
}));

View File

@ -1,54 +0,0 @@
import { FC } from 'react';
import { Typography, useTheme, useMediaQuery } from '@mui/material';
import Gradient from 'component/common/Gradient/Gradient';
import { ReactComponent as Logo } from 'assets/icons/logoWhiteBg.svg';
import { ReactComponent as LogoWithText } from 'assets/img/logoWhiteTransparentHorizontal.svg';
import { useStyles } from './StandaloneBanner.styles';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
interface IStandaloneBannerProps {
title: string;
}
const StandaloneBanner: FC<IStandaloneBannerProps> = ({ title, children }) => {
const theme = useTheme();
const { classes: styles } = useStyles();
const smallScreen = useMediaQuery(theme.breakpoints.down('md'));
return (
<Gradient
from={theme.palette.standaloneBannerGradient.from}
to={theme.palette.standaloneBannerGradient.to}
className={styles.gradient}
>
<div className={styles.container}>
<Typography variant="h1" className={styles.title}>
{title}
</Typography>
<Typography className={styles.bannerSubtitle}>
Committed to creating new ways of developing software
</Typography>
</div>
<div className={styles.logoContainer}>
<ConditionallyRender
condition={smallScreen}
show={
<LogoWithText
className={styles.logo}
aria-label="Unleash logo"
/>
}
elseShow={
<Logo
className={styles.logo}
aria-label="Unleash logo"
/>
}
/>
</div>
</Gradient>
);
};
export default StandaloneBanner;

View File

@ -1,20 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
userProfileMenu: {
display: 'flex',
},
profileContainer: {
position: 'relative',
},
button: {
color: 'inherit',
padding: '0.2rem 1rem',
'&:hover': {
backgroundColor: 'transparent',
},
},
icon: {
color: theme.palette.grey[700],
},
}));

View File

@ -1,20 +1,37 @@
import { useState } from 'react'; import { useState } from 'react';
import classnames from 'classnames';
import { Button, ClickAwayListener, styled } from '@mui/material'; import { Button, ClickAwayListener, styled } from '@mui/material';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import { useStyles } from 'component/user/UserProfile/UserProfile.styles';
import { useThemeStyles } from 'themes/themeStyles';
import { UserProfileContent } from './UserProfileContent/UserProfileContent'; import { UserProfileContent } from './UserProfileContent/UserProfileContent';
import { IUser } from 'interfaces/user'; import { IUser } from 'interfaces/user';
import { HEADER_USER_AVATAR } from 'utils/testIds'; import { HEADER_USER_AVATAR } from 'utils/testIds';
import { useId } from 'hooks/useId'; import { useId } from 'hooks/useId';
import { UserAvatar } from 'component/common/UserAvatar/UserAvatar'; import { UserAvatar } from 'component/common/UserAvatar/UserAvatar';
import { flexRow, focusable, itemsCenter } from 'themes/themeStyles';
const StyledUserAvatar = styled(UserAvatar)(({ theme }) => ({ const StyledUserAvatar = styled(UserAvatar)(({ theme }) => ({
width: theme.spacing(4.5), width: theme.spacing(4.5),
height: theme.spacing(4.5), height: theme.spacing(4.5),
})); }));
const StyledProfileContainer = styled('div')(({ theme }) => ({
position: 'relative',
}));
const StyledButton = styled(Button)(({ theme }) => ({
...focusable(theme),
...flexRow,
...itemsCenter,
color: 'inherit',
padding: theme.spacing(0.5, 2),
'&:hover': {
backgroundColor: 'transparent',
},
}));
const StyledIcon = styled(KeyboardArrowDownIcon)(({ theme }) => ({
color: theme.palette.neutral.main,
}));
interface IUserProfileProps { interface IUserProfileProps {
profile: IUser; profile: IUser;
} }
@ -23,19 +40,10 @@ const UserProfile = ({ profile }: IUserProfileProps) => {
const [showProfile, setShowProfile] = useState(false); const [showProfile, setShowProfile] = useState(false);
const modalId = useId(); const modalId = useId();
const { classes: styles } = useStyles();
const { classes: themeStyles } = useThemeStyles();
return ( return (
<ClickAwayListener onClickAway={() => setShowProfile(false)}> <ClickAwayListener onClickAway={() => setShowProfile(false)}>
<div className={styles.profileContainer}> <StyledProfileContainer>
<Button <StyledButton
className={classnames(
themeStyles.flexRow,
themeStyles.itemsCenter,
themeStyles.focusable,
styles.button
)}
onClick={() => setShowProfile(prev => !prev)} onClick={() => setShowProfile(prev => !prev)}
aria-controls={showProfile ? modalId : undefined} aria-controls={showProfile ? modalId : undefined}
aria-expanded={showProfile} aria-expanded={showProfile}
@ -46,15 +54,15 @@ const UserProfile = ({ profile }: IUserProfileProps) => {
user={profile} user={profile}
data-testid={HEADER_USER_AVATAR} data-testid={HEADER_USER_AVATAR}
/> />
<KeyboardArrowDownIcon className={styles.icon} /> <StyledIcon />
</Button> </StyledButton>
<UserProfileContent <UserProfileContent
id={modalId} id={modalId}
showProfile={showProfile} showProfile={showProfile}
setShowProfile={setShowProfile} setShowProfile={setShowProfile}
profile={profile} profile={profile}
/> />
</div> </StyledProfileContainer>
</ClickAwayListener> </ClickAwayListener>
); );
}; };

View File

@ -1,8 +1,6 @@
import { Typography } from '@mui/material'; import { styled, Typography } from '@mui/material';
import classnames from 'classnames';
import { Dispatch, SetStateAction, useEffect, useState } from 'react'; import { Dispatch, SetStateAction, useEffect, useState } from 'react';
import { BAD_REQUEST, OK } from 'constants/statusCodes'; import { BAD_REQUEST, OK } from 'constants/statusCodes';
import { useStyles } from './PasswordChecker.styles';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { formatApiPath } from 'utils/formatPath'; import { formatApiPath } from 'utils/formatPath';
import { Alert } from '@mui/material'; import { Alert } from '@mui/material';
@ -38,12 +36,64 @@ const REPEATING_CHARACTER_ERROR =
export const PASSWORD_FORMAT_MESSAGE = export const PASSWORD_FORMAT_MESSAGE =
'The password must be at least 10 characters long and must include an uppercase letter, a lowercase letter, a number, and a symbol.'; 'The password must be at least 10 characters long and must include an uppercase letter, a lowercase letter, a number, and a symbol.';
const StyledTitle = styled(Typography)(({ theme }) => ({
marginBottom: '0',
display: 'flex',
alignItems: 'center',
gap: '1ch',
}));
const StyledContainer = styled('div')(({ theme }) => ({
border: '1px solid #f1f1f1',
borderRadius: theme.shape.borderRadius,
position: 'relative',
maxWidth: '350px',
color: '#44606e',
}));
const StyledHeaderContainer = styled('div')(({ theme }) => ({
display: 'flex',
padding: theme.spacing(1),
}));
const StyledCheckContainer = styled('div')(({ theme }) => ({
width: '95px',
margin: theme.spacing(0, 0.5),
display: 'flex',
justifyContent: 'center',
}));
const StyledDivider = styled('div')(({ theme }) => ({
backgroundColor: theme.palette.neutral.light,
height: '1px',
width: '100%',
}));
const StyledStatusBarContainer = styled('div')(({ theme }) => ({
display: 'flex',
padding: theme.spacing(1),
}));
const StyledError = styled(Alert)(({ theme }) => ({
marginTop: theme.spacing(1),
bottom: '0',
position: 'absolute',
}));
const StyledStatusBar = styled('div', {
shouldForwardProp: prop => prop !== 'error',
})<{ error: boolean }>(({ theme, error }) => ({
width: '50px',
borderRadius: theme.shape.borderRadius,
height: '6px',
backgroundColor: error ? 'red' : theme.palette.primary.main,
}));
const PasswordChecker = ({ const PasswordChecker = ({
password, password,
callback, callback,
style = {}, style = {},
}: IPasswordCheckerProps) => { }: IPasswordCheckerProps) => {
const { classes: styles } = useStyles();
const [casingError, setCasingError] = useState(true); const [casingError, setCasingError] = useState(true);
const [numberError, setNumberError] = useState(true); const [numberError, setNumberError] = useState(true);
const [symbolError, setSymbolError] = useState(true); const [symbolError, setSymbolError] = useState(true);
@ -141,83 +191,63 @@ const PasswordChecker = ({
} }
}; };
const lengthStatusBarClasses = classnames(styles.statusBar, {
[styles.statusBarSuccess]: !lengthError,
});
const numberStatusBarClasses = classnames(styles.statusBar, {
[styles.statusBarSuccess]: !numberError,
});
const symbolStatusBarClasses = classnames(styles.statusBar, {
[styles.statusBarSuccess]: !symbolError,
});
const casingStatusBarClasses = classnames(styles.statusBar, {
[styles.statusBarSuccess]: !casingError,
});
return ( return (
<> <>
<Typography variant="body2" className={styles.title} data-loading> <StyledTitle variant="body2" data-loading>
Please set a strong password Please set a strong password
<HelpIcon tooltip={PASSWORD_FORMAT_MESSAGE} /> <HelpIcon tooltip={PASSWORD_FORMAT_MESSAGE} />
</Typography> </StyledTitle>
<div <StyledContainer
className={styles.container}
style={{ style={{
...style, ...style,
}} }}
> >
<div className={styles.headerContainer}> <StyledHeaderContainer>
<div className={styles.checkContainer}> <StyledCheckContainer>
<Typography variant="body2" data-loading> <Typography variant="body2" data-loading>
Length Length
</Typography> </Typography>
</div> </StyledCheckContainer>
<div className={styles.checkContainer}> <StyledCheckContainer>
<Typography variant="body2" data-loading> <Typography variant="body2" data-loading>
Casing Casing
</Typography> </Typography>
</div> </StyledCheckContainer>
<div className={styles.checkContainer}> <StyledCheckContainer>
<Typography variant="body2" data-loading> <Typography variant="body2" data-loading>
Number Number
</Typography> </Typography>
</div> </StyledCheckContainer>
<div className={styles.checkContainer}> <StyledCheckContainer>
<Typography variant="body2" data-loading> <Typography variant="body2" data-loading>
Symbol Symbol
</Typography> </Typography>
</div> </StyledCheckContainer>
</div> </StyledHeaderContainer>
<div className={styles.divider} /> <StyledDivider />
<div className={styles.statusBarContainer}> <StyledStatusBarContainer>
<div className={styles.checkContainer}> <StyledCheckContainer>
<div className={lengthStatusBarClasses} data-loading /> <StyledStatusBar error={lengthError} data-loading />
</div> </StyledCheckContainer>
<div className={styles.checkContainer}> <StyledCheckContainer>
<div className={casingStatusBarClasses} data-loading /> <StyledStatusBar error={casingError} data-loading />
</div>{' '} </StyledCheckContainer>{' '}
<div className={styles.checkContainer}> <StyledCheckContainer>
<div className={numberStatusBarClasses} data-loading /> <StyledStatusBar error={numberError} data-loading />
</div> </StyledCheckContainer>
<div className={styles.checkContainer}> <StyledCheckContainer>
<div className={symbolStatusBarClasses} data-loading /> <StyledStatusBar error={symbolError} data-loading />
</div> </StyledCheckContainer>
</div> </StyledStatusBarContainer>
<ConditionallyRender <ConditionallyRender
condition={repeatingCharError} condition={repeatingCharError}
show={ show={
<Alert <StyledError severity="error">
severity="error"
className={styles.repeatingError}
>
You may not repeat three characters in a row. You may not repeat three characters in a row.
</Alert> </StyledError>
} }
/> />
</div> </StyledContainer>
</> </>
); );
}; };

View File

@ -1,47 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
container: {
border: '1px solid #f1f1f1',
borderRadius: theme.shape.borderRadius,
position: 'relative',
maxWidth: '350px',
color: '#44606e',
},
headerContainer: { display: 'flex', padding: '0.5rem' },
divider: {
backgroundColor: theme.palette.neutral.light,
height: '1px',
width: '100%',
},
checkContainer: {
width: '95px',
margin: '0 0.25rem',
display: 'flex',
justifyContent: 'center',
},
statusBarContainer: {
display: 'flex',
padding: '0.5rem',
},
statusBar: {
width: '50px',
borderRadius: theme.shape.borderRadius,
backgroundColor: 'red',
height: '6px',
},
title: {
marginBottom: '0',
display: 'flex',
alignItems: 'center',
gap: '1ch',
},
statusBarSuccess: {
backgroundColor: theme.palette.primary.main,
},
repeatingError: {
marginTop: '0.5rem',
bottom: '0',
position: 'absolute',
},
}));

View File

@ -0,0 +1,60 @@
import { styled, Typography } from '@mui/material';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import CheckIcon from '@mui/icons-material/Check';
interface IPasswordMatcherProps {
started: boolean;
matchingPasswords: boolean;
}
const StyledMatcherContainer = styled('div')(({ theme }) => ({
position: 'relative',
paddingTop: theme.spacing(0.5),
}));
const StyledMatcher = styled(Typography, {
shouldForwardProp: prop => prop !== 'matchingPasswords',
})<{ matchingPasswords: boolean }>(({ theme, matchingPasswords }) => ({
position: 'absolute',
bottom: '-8px',
display: 'flex',
alignItems: 'center',
color: matchingPasswords
? theme.palette.primary.main
: theme.palette.error.main,
}));
const StyledMatcherCheckIcon = styled(CheckIcon)(({ theme }) => ({
marginRight: '5px',
}));
const PasswordMatcher = ({
started,
matchingPasswords,
}: IPasswordMatcherProps) => {
return (
<StyledMatcherContainer>
<ConditionallyRender
condition={started}
show={
<StyledMatcher
variant="body2"
data-loading
matchingPasswords={matchingPasswords}
>
<StyledMatcherCheckIcon />{' '}
<ConditionallyRender
condition={matchingPasswords}
show={<Typography> Passwords match</Typography>}
elseShow={
<Typography> Passwords do not match</Typography>
}
/>
</StyledMatcher>
}
/>
</StyledMatcherContainer>
);
};
export default PasswordMatcher;

View File

@ -1,23 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
matcherContainer: {
position: 'relative',
paddingTop: theme.spacing(0.5),
},
matcherIcon: {
marginRight: '5px',
},
matcher: {
position: 'absolute',
bottom: '-8px',
display: 'flex',
alignItems: 'center',
},
matcherError: {
color: theme.palette.error.main,
},
matcherSuccess: {
color: theme.palette.primary.main,
},
}));

View File

@ -1,55 +0,0 @@
import { Typography } from '@mui/material';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import classnames from 'classnames';
import CheckIcon from '@mui/icons-material/Check';
import { useStyles } from './PasswordMatcher.styles';
interface IPasswordMatcherProps {
started: boolean;
matchingPasswords: boolean;
}
const PasswordMatcher = ({
started,
matchingPasswords,
}: IPasswordMatcherProps) => {
const { classes: styles } = useStyles();
return (
<div className={styles.matcherContainer}>
<ConditionallyRender
condition={started}
show={
<ConditionallyRender
condition={matchingPasswords}
show={
<Typography
variant="body2"
data-loading
className={classnames(styles.matcher, {
[styles.matcherSuccess]: matchingPasswords,
})}
>
<CheckIcon className={styles.matcherIcon} />{' '}
Passwords match
</Typography>
}
elseShow={
<Typography
variant="body2"
data-loading
className={classnames(styles.matcher, {
[styles.matcherError]: !matchingPasswords,
})}
>
Passwords do not match
</Typography>
}
/>
}
/>
</div>
);
};
export default PasswordMatcher;

View File

@ -1,8 +1,8 @@
import { Button, styled } from '@mui/material'; import { Button, styled } from '@mui/material';
import React, { SyntheticEvent, useCallback, useEffect, useState } from 'react'; import React, { SyntheticEvent, useCallback, useEffect, useState } from 'react';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import PasswordChecker from './PasswordChecker/PasswordChecker'; import PasswordChecker from './PasswordChecker';
import PasswordMatcher from './PasswordMatcher/PasswordMatcher'; import PasswordMatcher from './PasswordMatcher';
import PasswordField from 'component/common/PasswordField/PasswordField'; import PasswordField from 'component/common/PasswordField/PasswordField';
interface IResetPasswordProps { interface IResetPasswordProps {

View File

@ -0,0 +1,49 @@
import { Alert, styled, Typography } from '@mui/material';
import { Link } from 'react-router-dom';
const StyledContainer = styled('div')(({ theme }) => ({
margin: 'auto auto 0 auto',
width: '230px',
[theme.breakpoints.down('md')]: {
marginTop: theme.spacing(2),
},
}));
const StyledLink = styled(Link)(({ theme }) => ({
fontWeight: 'bold',
textAlign: 'center',
}));
const StyledTypography = styled(Typography)(({ theme }) => ({
fontWeight: 'bold',
marginBottom: theme.spacing(1),
}));
const StyledRef = styled('a')(({ theme }) => ({
fontWeight: 'bold',
textAlign: 'center',
}));
const SecondaryLoginActions = () => {
return (
<StyledContainer>
<StyledLink to="/forgotten-password">
<StyledTypography variant="body2">
Forgot password?
</StyledTypography>
</StyledLink>
<Typography variant="body2">
Don't have an account?{' '}
<StyledRef
href="https://www.getunleash.io/plans"
target="_blank"
rel="noopener noreferrer"
>
Sign up
</StyledRef>
</Typography>
</StyledContainer>
);
};
export default SecondaryLoginActions;

View File

@ -1,16 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
container: {
margin: 'auto auto 0 auto',
width: '230px',
[theme.breakpoints.down('md')]: {
marginTop: '1rem',
},
},
link: {
fontWeight: 'bold',
textAlign: 'center',
},
text: { fontWeight: 'bold', marginBottom: '0.5rem' },
}));

View File

@ -1,29 +0,0 @@
import { Typography } from '@mui/material';
import { Link } from 'react-router-dom';
import { useStyles } from './SecondaryLoginActions.styles';
const SecondaryLoginActions = () => {
const { classes: 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

@ -0,0 +1,83 @@
import { FC } from 'react';
import StandaloneBanner from 'component/user/StandaloneBanner';
import { styled } from '@mui/material';
interface IStandaloneLayout {
BannerComponent?: JSX.Element;
showMenu?: boolean;
}
const StyledContainer = styled('div')(({ theme }) => ({
padding: theme.spacing(11),
background: theme.palette.standaloneBackground,
display: 'flex',
[theme.breakpoints.down('md')]: {
flexDirection: 'column',
},
[theme.breakpoints.down('sm')]: {
padding: '0',
},
minHeight: '100vh',
}));
const StyledHeader = styled('header')(({ theme }) => ({
width: '40%',
borderRadius: theme.shape.borderRadius,
[theme.breakpoints.down('md')]: {
borderRadius: '0',
width: '100%',
minHeight: 'auto',
},
}));
const StyledMain = styled('main')(({ theme }) => ({
width: '60%',
flex: '1',
borderTopRightRadius: '3px',
borderBottomRightRadius: '3px',
backgroundColor: theme.palette.background.paper,
position: 'relative',
[theme.breakpoints.down('md')]: {
borderRadius: '0',
width: '100%',
position: 'static',
minHeight: 'auto',
},
}));
const StyledInnerRightContainer = styled('div')(({ theme }) => ({
display: 'flex',
justifyContent: 'center',
height: '100%',
padding: theme.spacing(12, 6),
[theme.breakpoints.down('md')]: {
padding: theme.spacing(4, 4),
},
[theme.breakpoints.down('sm')]: {
padding: theme.spacing(4, 2),
},
}));
const StandaloneLayout: FC<IStandaloneLayout> = ({
children,
BannerComponent,
}) => {
let banner = <StandaloneBanner title="Unleash" />;
if (BannerComponent) {
banner = BannerComponent;
}
return (
<StyledContainer>
<StyledHeader>{banner}</StyledHeader>
<StyledMain>
<StyledInnerRightContainer>
{children}
</StyledInnerRightContainer>
</StyledMain>
</StyledContainer>
);
};
export default StandaloneLayout;

View File

@ -1,56 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
container: {
padding: '5.5rem',
background: theme.palette.standaloneBackground,
display: 'flex',
[theme.breakpoints.down('md')]: {
flexDirection: 'column',
},
[theme.breakpoints.down('sm')]: {
padding: '0',
},
minHeight: '100vh',
},
leftContainer: {
width: '40%',
borderRadius: theme.shape.borderRadius,
[theme.breakpoints.down('md')]: {
borderRadius: '0',
width: '100%',
minHeight: 'auto',
},
},
rightContainer: {
width: '60%',
flex: '1',
borderTopRightRadius: '3px',
borderBottomRightRadius: '3px',
backgroundColor: theme.palette.background.paper,
position: 'relative',
[theme.breakpoints.down('md')]: {
borderRadius: '0',
width: '100%',
position: 'static',
minHeight: 'auto',
},
},
title: {
fontWeight: 'bold',
fontSize: '1.2rem',
marginBottom: '1rem',
},
innerRightContainer: {
display: 'flex',
justifyContent: 'center',
height: '100%',
padding: '6rem 3rem',
[theme.breakpoints.down('md')]: {
padding: '2rem 2rem',
},
[theme.breakpoints.down('sm')]: {
padding: '2rem 1rem',
},
},
}));

View File

@ -1,32 +0,0 @@
import { FC } from 'react';
import StandaloneBanner from 'component/user/StandaloneBanner/StandaloneBanner';
import { useStyles } from './StandaloneLayout.styles';
interface IStandaloneLayout {
BannerComponent?: JSX.Element;
showMenu?: boolean;
}
const StandaloneLayout: FC<IStandaloneLayout> = ({
children,
BannerComponent,
}) => {
const { classes: styles } = useStyles();
let banner = <StandaloneBanner title="Unleash" />;
if (BannerComponent) {
banner = BannerComponent;
}
return (
<div className={styles.container}>
<header className={styles.leftContainer}>{banner}</header>
<main className={styles.rightContainer}>
<div className={styles.innerRightContainer}>{children}</div>
</main>
</div>
);
};
export default StandaloneLayout;

View File

@ -11,11 +11,37 @@ export const focusable = (theme: Theme) => ({
}, },
}); });
export const contentSpacingY = (theme: Theme) => ({
'& > *': {
marginTop: `${theme.spacing(1)} !important`,
marginBottom: `${theme.spacing(1)} !important`,
},
});
export const title = (theme: Theme) => ({
fontSize: theme.fontSizes.mainHeader,
fontWeight: 'bold',
marginBottom: theme.spacing(1),
});
export const textCenter = {
textAlign: 'center',
} as const;
export const flexRow = { export const flexRow = {
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
} as const; } as const;
export const flexColumn = {
display: 'flex',
flexDirection: 'column',
} as const;
export const itemsCenter = {
alignItems: 'center',
} as const;
export const defaultBorderRadius = (theme: Theme) => ({ export const defaultBorderRadius = (theme: Theme) => ({
borderRadius: `${theme.shape.borderRadius}px`, borderRadius: `${theme.shape.borderRadius}px`,
}); });
@ -40,18 +66,6 @@ export const useThemeStyles = makeStyles()(theme => ({
marginBottom: '0.5rem !important', marginBottom: '0.5rem !important',
}, },
}, },
contentSpacingYLarge: {
'& > *': {
marginTop: '1.5rem !important',
marginBottom: '1.5rem !important',
},
},
contentSpacingX: {
'& > *': {
marginRight: '0.8rem !important',
marginLeft: '0.8rem !important',
},
},
relative: { relative: {
position: 'relative', position: 'relative',
}, },
@ -101,19 +115,6 @@ export const useThemeStyles = makeStyles()(theme => ({
fontWeight: 'bold', fontWeight: 'bold',
marginBottom: '0.5rem', marginBottom: '0.5rem',
}, },
fadeInBottomStartNoPosition: {
transform: 'translateY(400px)',
opacity: '0',
boxShadow: `rgb(129 129 129 / 40%) 4px 5px 11px 4px`,
zIndex: 500,
width: '100%',
backgroundColor: '#fff',
right: 0,
bottom: 0,
left: 0,
height: '300px',
position: 'fixed',
},
fadeInBottomStart: { fadeInBottomStart: {
opacity: '0', opacity: '0',
position: 'fixed', position: 'fixed',