✨ Migrate to TanStack Query (React Query) and TanStack Router (#637)
This commit is contained in:
99
new-frontend/src/routes/login.tsx
Normal file
99
new-frontend/src/routes/login.tsx
Normal file
@@ -0,0 +1,99 @@
|
||||
import React from 'react';
|
||||
|
||||
import { ViewIcon, ViewOffIcon } from '@chakra-ui/icons';
|
||||
import { Button, Center, Container, FormControl, FormErrorMessage, Icon, Image, Input, InputGroup, InputRightElement, Link, useBoolean } from '@chakra-ui/react';
|
||||
import { Link as RouterLink, createFileRoute, redirect } from '@tanstack/react-router';
|
||||
import { SubmitHandler, useForm } from 'react-hook-form';
|
||||
|
||||
import Logo from '../assets/images/fastapi-logo.svg';
|
||||
import { ApiError } from '../client';
|
||||
import { Body_login_login_access_token as AccessToken } from '../client/models/Body_login_login_access_token';
|
||||
import useAuth, { isLoggedIn } from '../hooks/useAuth';
|
||||
|
||||
export const Route = createFileRoute('/login')({
|
||||
component: Login,
|
||||
beforeLoad: async () => {
|
||||
if (isLoggedIn()) {
|
||||
throw redirect({
|
||||
to: '/',
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function Login() {
|
||||
const [show, setShow] = useBoolean();
|
||||
const { login } = useAuth();
|
||||
const [error, setError] = React.useState<string | null>(null);
|
||||
const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm<AccessToken>({
|
||||
mode: 'onBlur',
|
||||
criteriaMode: 'all',
|
||||
defaultValues: {
|
||||
username: '',
|
||||
password: ''
|
||||
}
|
||||
});
|
||||
|
||||
const onSubmit: SubmitHandler<AccessToken> = async (data) => {
|
||||
try {
|
||||
await login(data);
|
||||
} catch (err) {
|
||||
const errDetail = (err as ApiError).body.detail;
|
||||
setError(errDetail)
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Container
|
||||
as='form'
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
h='100vh'
|
||||
maxW='sm'
|
||||
alignItems='stretch'
|
||||
justifyContent='center'
|
||||
gap={4}
|
||||
centerContent
|
||||
>
|
||||
<Image src={Logo} alt='FastAPI logo' height='auto' maxW='2xs' alignSelf='center' mb={4} />
|
||||
<FormControl id='username' isInvalid={!!errors.username || !!error}>
|
||||
<Input id='username' {...register('username', { pattern: { value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i, message: 'Invalid email address' } })} placeholder='Email' type='text' />
|
||||
{errors.username && <FormErrorMessage>{errors.username.message}</FormErrorMessage>}
|
||||
</FormControl>
|
||||
<FormControl id='password' isInvalid={!!error}>
|
||||
<InputGroup>
|
||||
<Input
|
||||
{...register('password')}
|
||||
type={show ? 'text' : 'password'}
|
||||
|
||||
placeholder='Password'
|
||||
/>
|
||||
<InputRightElement
|
||||
color='gray.400'
|
||||
_hover={{
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
<Icon onClick={setShow.toggle} aria-label={show ? 'Hide password' : 'Show password'}>
|
||||
{show ? <ViewOffIcon /> : <ViewIcon />}
|
||||
</Icon>
|
||||
</InputRightElement>
|
||||
</InputGroup>
|
||||
{error && <FormErrorMessage>
|
||||
{error}
|
||||
</FormErrorMessage>}
|
||||
</FormControl>
|
||||
<Center>
|
||||
<Link as={RouterLink} to='/recover-password' color='blue.500'>
|
||||
Forgot password?
|
||||
</Link>
|
||||
</Center>
|
||||
<Button bg='ui.main' color='white' _hover={{ opacity: 0.8 }} type='submit' isLoading={isSubmitting}>
|
||||
Log In
|
||||
</Button>
|
||||
</Container>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Login;
|
Reference in New Issue
Block a user