Skip to content

Commit

Permalink
Merge pull request #6 from fga-eps-mds/17_auth_usuario
Browse files Browse the repository at this point in the history
feat: login page
  • Loading branch information
Matheusafonsouza authored Dec 8, 2024
2 parents ab6c910 + 9bc701d commit b8d8e75
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 2 deletions.
2 changes: 2 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Warnings from "./pages/Warnings"
import Loans from "./pages/Loans"
import { Toaster } from "./components/ui/toaster"
import PrivateRoute from './PrivateRoute';
import SignIn from "./pages/SignIn"

function App() {
return (
Expand All @@ -19,6 +20,7 @@ function App() {
<BrowserRouter>
<Routes>
<Route path="*" element={<NotFound />} />
<Route path="/login" element={<SignIn />} />
<Route path="/cadastro" element={<SignUp />} />
<Route path="/inicio" element={<PrivateRoute><Home /></PrivateRoute>} />
<Route path="/perfil" element={<PrivateRoute><Profile /></PrivateRoute>} />
Expand Down
2 changes: 1 addition & 1 deletion src/PrivateRoute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const PrivateRoute = ({ children }: { children: any }) => {

useEffect(() => {
if (token) return;
navigate('/cadastro');
navigate('/login');
})

return children;
Expand Down
14 changes: 14 additions & 0 deletions src/hooks/useApi/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,20 @@ const useApi = () => {
.catch((err) => resolve(getDefaultErrorUseAPIMessage(err)));
});
},
signIn: (data: {
email: string;
password: string
}): Promise<{ data: {
accessToken: string;
refreshToken: string;
} }> => {
return new Promise((resolve) => {
api
.post('/auth/signin', data)
.then((res) => resolve(res))
.catch((err) => resolve(getDefaultErrorUseAPIMessage(err)));
});
},
editProfile: async (id: string, data: {
firstName: string;
lastName: string;
Expand Down
30 changes: 29 additions & 1 deletion src/hooks/useAuth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ interface SignUpParams {
password: string;
}

interface SignInParams {
email: string;
password: string;
}

interface EditProfileParams {
firstName: string;
lastName: string;
Expand All @@ -27,13 +32,18 @@ type AuthContextType = {
token: string | null;
signOut: () => void;
signUp: (userToSignUp: SignUpParams) => Promise<boolean>;
signIn: (userToSignIn: SignInParams) => Promise<boolean>;
editProfile: (id: string, profileToEdit: EditProfileParams) => Promise<boolean>;
};

const AuthContext = createContext({} as AuthContextType);

export const AuthProvider = ({ children }: { children: ReactNode }) => {
const { signUp: authSignUp, editProfile: authEditProfile } = useApi();
const {
signUp: authSignUp,
signIn: authSignIn,
editProfile: authEditProfile,
} = useApi();

const localToken =
typeof window !== 'undefined'
Expand All @@ -60,6 +70,23 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => {
return true;
}

async function signIn(userToSignIn: SignInParams): Promise<boolean> {
const { data } = await authSignIn(userToSignIn);
if (!data.accessToken) {
toaster.create({
title: 'Erro ao realizar login',
description: 'Verifique os campos e tente novamente.',
type: 'error',
})
return false;
};
setToken(data.accessToken);
if (typeof window !== 'undefined') {
localStorage.setItem('@livrolivre:token', data.accessToken);
}
return true;
}

async function editProfile(id: string, profileToEdit: EditProfileParams): Promise<boolean> {
const { data } = await authEditProfile(id, profileToEdit);
if (data.id) {
Expand Down Expand Up @@ -92,6 +119,7 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => {
signOut,
signUp,
editProfile,
signIn,
}}
>
{children}
Expand Down
82 changes: 82 additions & 0 deletions src/pages/SignIn/SignInForm/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { useEffect, useState } from 'react';
import { useAuth } from '../../../hooks/useAuth';
import { useNavigate } from 'react-router';
import { Input, Stack } from '@chakra-ui/react';
import { useForm } from 'react-hook-form';
import { PasswordInput } from '../../../components/ui/password-input';
import { Button } from '../../../components/ui/button';
import { Field } from '../../../components/ui/field';

interface FormValues {
email: string;
password: string;
}

function SignInForm() {
const [loading, setLoading] = useState(false);
const navigate = useNavigate();

const {
register,
handleSubmit,
formState: { errors, isValid },
} = useForm<FormValues>();

const { signIn, token } = useAuth();

const onSubmit = handleSubmit(async (data: FormValues) => {
setLoading(true);
await signIn({
email: data.email,
password: data.password,
});
setLoading(false);
})

useEffect(() => {
if (!token) return;
navigate('/inicio');
}, [token])

return (
<form onSubmit={onSubmit}>
<Stack gap={'40px'}>
<Stack gap={'10px'}>
<Field invalid={!!errors.email} errorText={errors.email?.message}>
<Input
size={'2xl'}
placeholder={'E-mail'}
{...register('email', {
required: "Campo obrigatório.",
pattern: {
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i,
message: "E-mail inválido."
}
})}
/>
</Field>
<Field invalid={!!errors.password} errorText={errors.password?.message}>
<PasswordInput
size={'2xl'}
placeholder={'Senha'}
{...register('password', { required: "Campo obrigatório." })}
/>
</Field>
</Stack>
<Button
loading={loading}
type="submit"
width={'100%'}
size={'2xl'}
bg={'green.100'}
fontWeight={'semibold'}
disabled={!isValid}
>
Entrar
</Button>
</Stack>
</form>
);
}

export default SignInForm
12 changes: 12 additions & 0 deletions src/pages/SignIn/SignInHeader/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Stack, Text } from '@chakra-ui/react';

function SignUpHeader() {
return (
<Stack gap={'5px'}>
<Text textStyle={'3xl'} fontWeight={'semibold'} color={'blue.100'}>Bem vindo ao Livro Livre</Text>
<Text color={'blue.100'}>Insira seus dados para fazer o login e começar a ajudar utilizar o Livro Livre</Text>
</Stack>
);
}

export default SignUpHeader
20 changes: 20 additions & 0 deletions src/pages/SignIn/SignUpButton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Link } from 'react-router';
import { Center, Link as ChakraLink, Separator, Stack, Text } from '@chakra-ui/react';

function SignUpButton() {
return (
<Center width={'100%'}>
<Stack gap={'25px'} width={'100%'}>
<Separator />
<Text
textAlign={'center'}
color={'blue.100'}
>
Não possuí uma conta? <ChakraLink color={'green.100'}><Link to='/cadastro'>Faça seu cadastro aqui</Link></ChakraLink>
</Text>
</Stack>
</Center>
);
}

export default SignUpButton
20 changes: 20 additions & 0 deletions src/pages/SignIn/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Box, Center, Stack } from '@chakra-ui/react';
import SignInForm from './SignInForm';
import SignUpButton from './SignUpButton';
import SignInHeader from './SignInHeader';

function SignIn() {
return (
<Box padding='40px'>
<Center>
<Stack gap={'40px'}>
<SignInHeader />
<SignInForm />
<SignUpButton />
</Stack>
</Center>
</Box>
);
}

export default SignIn

0 comments on commit b8d8e75

Please sign in to comment.