206 lines
6.9 KiB
TypeScript
206 lines
6.9 KiB
TypeScript
import { ReactElement, Suspense, useContext, useState } from 'react';
|
|
import {
|
|
Alert,
|
|
Button,
|
|
FormControl,
|
|
IconButton,
|
|
InputAdornment,
|
|
InputLabel,
|
|
Link,
|
|
Skeleton,
|
|
Stack,
|
|
TextField,
|
|
Typography,
|
|
} from '@mui/material';
|
|
import loginBanner from 'assets/authentication-banners/green.png';
|
|
import IconifyIcon from 'components/base/IconifyIcon';
|
|
import logo from 'assets/logo/favicon-logo.png';
|
|
import Image from 'components/base/Image';
|
|
import { axiosInstance } from '../../axiosApi.js';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import { Form, Formik } from 'formik';
|
|
import { AuthContext } from 'contexts/AuthContext.js';
|
|
|
|
type loginValues = {
|
|
email: string;
|
|
password: string;
|
|
};
|
|
|
|
const Login = (): ReactElement => {
|
|
const [showPassword, setShowPassword] = useState(false);
|
|
|
|
const handleClickShowPassword = () => setShowPassword(!showPassword);
|
|
|
|
const [errorMessage, setErrorMessage] = useState<any | null>(null);
|
|
const navigate = useNavigate();
|
|
const { setAuthentication } = useContext(AuthContext);
|
|
|
|
const handleLogin = async ({ email, password }: loginValues): Promise<void> => {
|
|
try {
|
|
const response = await axiosInstance.post('/token/', {
|
|
email: email,
|
|
password: password,
|
|
});
|
|
axiosInstance.defaults.headers['Authorization'] = 'JWT ' + response.data.access;
|
|
localStorage.setItem('access_token', response.data.access);
|
|
localStorage.setItem('refresh_token', response.data.refresh);
|
|
const get_user_response = await axiosInstance.get('/user/');
|
|
|
|
setAuthentication(true);
|
|
|
|
navigate('/dashboard');
|
|
} catch (error) {
|
|
const hasErrors = Object.keys(error.response.data).length > 0;
|
|
if (hasErrors) {
|
|
setErrorMessage(error.response.data);
|
|
} else {
|
|
setErrorMessage(null);
|
|
}
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Stack
|
|
direction="row"
|
|
bgcolor="background.paper"
|
|
boxShadow={(theme) => theme.shadows[3]}
|
|
height={560}
|
|
width={{ md: 960 }}
|
|
>
|
|
<Stack width={{ md: 0.5 }} m={2.5} gap={10}>
|
|
<Link href="/" height="fit-content">
|
|
<Image src={logo} width={82.6} />
|
|
</Link>
|
|
<Stack alignItems="center" gap={2.5} width={330} mx="auto">
|
|
<Typography variant="h3">Login</Typography>
|
|
<Formik
|
|
initialValues={{
|
|
email: '',
|
|
password: '',
|
|
}}
|
|
onSubmit={handleLogin}
|
|
>
|
|
{({ setFieldValue }) => (
|
|
<Form>
|
|
<FormControl variant="standard" fullWidth>
|
|
{errorMessage ? (
|
|
<Alert severity="error">
|
|
{errorMessage.detail ? (
|
|
<Typography>{errorMessage.detail}</Typography>
|
|
) : (
|
|
<ul>
|
|
{Object.entries(errorMessage).map(([fieldName, errorMessages]) => (
|
|
<li key={fieldName}>
|
|
<strong>{fieldName}</strong>
|
|
{Array.isArray(errorMessages) ? (
|
|
<ul>
|
|
{errorMessages.map((message, index) => (
|
|
<li key={`${fieldName}-${index}`}>{message}</li>
|
|
))}
|
|
</ul>
|
|
) : (
|
|
<span>: {String(errorMessages)}</span>
|
|
)}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
)}
|
|
</Alert>
|
|
) : null}
|
|
<InputLabel shrink htmlFor="email">
|
|
Email
|
|
</InputLabel>
|
|
<TextField
|
|
variant="filled"
|
|
placeholder="Enter your email"
|
|
id="email"
|
|
onChange={(event) => setFieldValue('email', event.target.value)}
|
|
InputProps={{
|
|
endAdornment: (
|
|
<InputAdornment position="end">
|
|
<IconifyIcon icon="ic:baseline-email" />
|
|
</InputAdornment>
|
|
),
|
|
}}
|
|
/>
|
|
</FormControl>
|
|
<FormControl variant="standard" fullWidth>
|
|
<InputLabel shrink htmlFor="password">
|
|
Password
|
|
</InputLabel>
|
|
<TextField
|
|
variant="filled"
|
|
placeholder="********"
|
|
onChange={(event) => setFieldValue('password', event.target.value)}
|
|
type={showPassword ? 'text' : 'password'}
|
|
id="password"
|
|
InputProps={{
|
|
endAdornment: (
|
|
<InputAdornment position="end">
|
|
<IconButton
|
|
aria-label="toggle password visibility"
|
|
onClick={handleClickShowPassword}
|
|
edge="end"
|
|
sx={{
|
|
color: 'text.secondary',
|
|
}}
|
|
>
|
|
{showPassword ? (
|
|
<IconifyIcon icon="ic:baseline-key-off" />
|
|
) : (
|
|
<IconifyIcon icon="ic:baseline-key" />
|
|
)}
|
|
</IconButton>
|
|
</InputAdornment>
|
|
),
|
|
}}
|
|
/>
|
|
</FormControl>
|
|
<Typography
|
|
variant="body1"
|
|
sx={{
|
|
alignSelf: 'flex-end',
|
|
}}
|
|
>
|
|
<Link href="/authentication/forgot-password" underline="hover">
|
|
Forget password
|
|
</Link>
|
|
</Typography>
|
|
<Button variant="contained" type={'submit'} fullWidth>
|
|
Log in
|
|
</Button>
|
|
</Form>
|
|
)}
|
|
</Formik>
|
|
<Typography variant="body2" color="text.secondary">
|
|
Don't have an account ?{' '}
|
|
<Link
|
|
href="/authentication/sign-up"
|
|
underline="hover"
|
|
fontSize={(theme) => theme.typography.body1.fontSize}
|
|
>
|
|
Sign up
|
|
</Link>
|
|
</Typography>
|
|
</Stack>
|
|
</Stack>
|
|
<Suspense
|
|
fallback={
|
|
<Skeleton variant="rectangular" height={1} width={1} sx={{ bgcolor: 'primary.main' }} />
|
|
}
|
|
>
|
|
<Image
|
|
alt="Login banner"
|
|
src={loginBanner}
|
|
sx={{
|
|
width: 0.5,
|
|
display: { xs: 'none', md: 'block' },
|
|
}}
|
|
/>
|
|
</Suspense>
|
|
</Stack>
|
|
);
|
|
};
|
|
|
|
export default Login;
|