1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-04-10 01:16:39 +02:00
unleash.unleash/frontend/src/component/user/HostedAuth.tsx
2024-11-15 09:57:17 +01:00

177 lines
6.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { type FormEventHandler, useState, type VFC } from 'react';
import { Button, Grid, styled, TextField, Typography } from '@mui/material';
import { useNavigate } from 'react-router';
import useQueryParams from 'hooks/useQueryParams';
import AuthOptions from './common/AuthOptions/AuthOptions';
import DividerText from 'component/common/DividerText/DividerText';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import PasswordField from 'component/common/PasswordField/PasswordField';
import { useAuthApi } from 'hooks/api/actions/useAuthApi/useAuthApi';
import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser';
import { LOGIN_BUTTON, LOGIN_EMAIL_ID, LOGIN_PASSWORD_ID } from 'utils/testIds';
import type { IAuthEndpointDetailsResponse } from 'hooks/api/getters/useAuth/useAuthEndpoint';
import { BadRequestError, NotFoundError } from 'utils/apiUtils';
import { contentSpacingY } from 'themes/themeStyles';
import useToast from 'hooks/useToast';
interface IHostedAuthProps {
authDetails: IAuthEndpointDetailsResponse;
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 { refetchUser } = useAuthUser();
const navigate = useNavigate();
const params = useQueryParams();
const { passwordAuth } = useAuthApi();
const [username, setUsername] = useState(params.get('email') || '');
const [password, setPassword] = useState('');
const [errors, setErrors] = useState<{
usernameError?: string;
passwordError?: string;
apiError?: string;
}>({});
const { setToastData } = useToast();
const handleSubmit: FormEventHandler<HTMLFormElement> = async (evt) => {
evt.preventDefault();
if (!username) {
setErrors((prev) => ({
...prev,
usernameError: 'This is a required field',
}));
}
if (!password) {
setErrors((prev) => ({
...prev,
passwordError: 'This is a required field',
}));
}
if (!password || !username) {
return;
}
try {
const data = await passwordAuth(
authDetails.path,
username,
password,
);
if (data.deletedSessions && data.activeSessions) {
setToastData({
type: 'success',
title: 'Maximum Session Limit Reached',
text: `You can have up to ${data.activeSessions} active sessions at a time. To enhance your account security, weve ended ${data.deletedSessions} session(s) on other browsers.`,
});
}
refetchUser();
navigate(redirect, { replace: true });
} catch (error: any) {
if (
error instanceof NotFoundError ||
error instanceof BadRequestError
) {
setErrors((prev) => ({
...prev,
apiError: 'Invalid login details',
}));
setPassword('');
setUsername('');
} else {
setErrors({
apiError: 'Unknown error while trying to authenticate.',
});
}
}
};
const { usernameError, passwordError, apiError } = errors;
const { options = [] } = authDetails;
return (
<>
<ConditionallyRender
condition={options.length > 0}
show={
<>
<AuthOptions options={options} />
<DividerText text='or signin with username' />
</>
}
/>
<ConditionallyRender
condition={!authDetails.defaultHidden}
show={
<form onSubmit={handleSubmit}>
<StyledTypography variant='subtitle2'>
{apiError}
</StyledTypography>
<StyledDiv>
<TextField
label='Username or email'
name='username'
id='username'
type='text'
onChange={(evt) =>
setUsername(evt.target.value)
}
value={username}
error={Boolean(usernameError)}
helperText={usernameError}
variant='outlined'
size='small'
data-testid={LOGIN_EMAIL_ID}
/>
<PasswordField
label='Password'
onChange={(evt) =>
setPassword(evt.target.value)
}
name='password'
id='password'
value={password}
error={Boolean(passwordError)}
helperText={passwordError}
autoComplete='current-password'
data-testid={LOGIN_PASSWORD_ID}
/>
<Grid container>
<StyledButton
variant='contained'
color='primary'
type='submit'
data-testid={LOGIN_BUTTON}
>
Sign in
</StyledButton>
</Grid>
</StyledDiv>
</form>
}
/>
</>
);
};
export default HostedAuth;