From 644315c6b55cd41d5944d56365b38e0103c19586 Mon Sep 17 00:00:00 2001 From: leonardomessias98 Date: Sun, 4 Apr 2021 11:20:09 -0300 Subject: [PATCH 1/2] adjusts in login style and mobile responsivity --- src/contexts/AuthContext.tsx | 37 ++++++++-------- src/pages/Login/index.tsx | 3 +- src/pages/Login/styles.ts | 84 ++++++++++++++++++++---------------- 3 files changed, 66 insertions(+), 58 deletions(-) diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx index 1bd6552..66b2e0e 100644 --- a/src/contexts/AuthContext.tsx +++ b/src/contexts/AuthContext.tsx @@ -39,25 +39,22 @@ const AuthProvider = ({ children }: AuthProviderProps) => { setData({ token, user }); }, []); - const signUp = useCallback( - async credentials => { - try { - const response = await api.post('/users', credentials); - - setSuccess(true); - localStorage.setItem('@Typext:user', JSON.stringify(response.data)); - localStorage.setItem('@Typext:token', credentials.token); - } catch (err) { - const errorStatus = err.response?.status; - - if (errorStatus === 401) { - setError(err.response?.data.message); - setSuccess(false); - } + const signUp = useCallback(async credentials => { + try { + const response = await api.post('/users', credentials); + + setSuccess(true); + localStorage.setItem('@Typext:user', JSON.stringify(response.data)); + localStorage.setItem('@Typext:token', credentials.token); + } catch (err) { + const errorStatus = err.response?.status; + + if (errorStatus === 401) { + setError(err.response?.data.message); + setSuccess(false); } - }, - [], - ); + } + }, []); const signOut = useCallback(() => { localStorage.removeItem('@Typext:token'); @@ -78,7 +75,7 @@ const AuthProvider = ({ children }: AuthProviderProps) => { const inviteData = response.data; - localStorage.set( + localStorage.setItem( '@Typext:invite_data', JSON.stringify({ name: inviteData.name, email: inviteData.email }), ); @@ -88,7 +85,7 @@ const AuthProvider = ({ children }: AuthProviderProps) => { const errorStatus = err.response?.status; if (errorStatus === 401) { - setError(err.response.data.message); + setError(err.response?.data.message); setSuccess(false); } } diff --git a/src/pages/Login/index.tsx b/src/pages/Login/index.tsx index 4232087..bf686f4 100644 --- a/src/pages/Login/index.tsx +++ b/src/pages/Login/index.tsx @@ -45,8 +45,7 @@ function Login() { Esqueceu a senha? - {' '} - Clique aqui. + Clique aqui. diff --git a/src/pages/Login/styles.ts b/src/pages/Login/styles.ts index b2fe4f9..0a088c6 100644 --- a/src/pages/Login/styles.ts +++ b/src/pages/Login/styles.ts @@ -1,22 +1,25 @@ import styled from 'styled-components'; const Content = styled.div` - display: flex; justify-content: center; - margin-top: 4.375rem; - + padding: 30px; .Login { - display: flex; align-items: center; justify-content: center; flex-direction: column; + &, + .EmailPassword, + .LoginPassForgot { + width: 100%; + max-width: 50rem; + } - a { + a { width: 18.125rem; height: 3.125rem; @@ -27,51 +30,60 @@ const Content = styled.div` } .EmailPassword { + margin: 10rem; - margin-top: 13.25rem; - - display: flex; - flex-direction: column; - align-items: flex-start; - - Input { - margin-bottom: 2rem; - } - + display: flex; + flex-direction: column; + align-items: flex-start; + Input { + margin-bottom: 2rem; + } } - .LoginPassForgot { + .LoginPassForgot { + width: 100%; - margin-top: 4rem; - width: 100%; + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + Button { + width: 14.063rem; + } + + a { display: flex; + flex-wrap: wrap; align-items: center; - justify-content: space-between; - - Button { - width: 14.063rem; - } - - a { - display: flex; - align-items: center; - color: var(--red-pink); - } - - strong { - margin-left: 0.2rem; - } + width: max-content; + text-align: center; + color: var(--red-pink); + } + strong { + margin-left: 0.2rem; + } } + } - - + @media (max-width: 1024px) { + align-items: center; + width: 100%; + height: 100%; + margin: 0; } - + @media (max-width: 410px) { + .LoginPassForgot { + justify-content: center !important; + button { + margin-bottom: 2rem; + } + } + } `; export default Content; From e3bb2dcd84c94159287388e651225488c4f6ab71 Mon Sep 17 00:00:00 2001 From: leonardomessias98 Date: Mon, 5 Apr 2021 09:30:15 -0300 Subject: [PATCH 2/2] linking login with backend and improving code quality --- src/DTOs/Auth.tsx | 8 +- src/components/Header/index.tsx | 2 +- src/components/InputForm/index.tsx | 35 ++--- src/components/InputForm/styles.ts | 17 +-- src/components/Tooltip/styles.ts | 9 +- src/contexts/AuthContext.tsx | 80 ++++++----- .../InviteConfirmationModal/index.tsx | 10 +- src/pages/Login/index.tsx | 127 ++++++++++++------ src/pages/Login/loginSchema.ts | 10 ++ src/pages/Login/styles.ts | 6 +- .../components/RegisterModal/index.tsx | 16 +-- src/pages/RegisterNewUser/index.tsx | 3 +- src/services/api.ts | 6 + src/services/auth.ts | 6 + 14 files changed, 199 insertions(+), 136 deletions(-) create mode 100644 src/pages/Login/loginSchema.ts diff --git a/src/DTOs/Auth.tsx b/src/DTOs/Auth.tsx index a6f6654..f9bdd75 100644 --- a/src/DTOs/Auth.tsx +++ b/src/DTOs/Auth.tsx @@ -29,10 +29,10 @@ export interface SignUpCredentials { export interface AuthContextData { user: object; - invitation: { error: string; loader: boolean; success: boolean }; - register: { error: string; loader: boolean; success: boolean }; - signIn(crendentials: SignInCredentials): Promise; - signUp(crendentials: SignUpCredentials): Promise; + invitation: { error: string; loader: boolean }; + register: { error: string; loader: boolean }; + signIn(crendentials: SignInCredentials): Promise; + signUp(crendentials: SignUpCredentials): Promise; inviteUser(crendentials: InviteUserCredentials): Promise; signOut(): void; } diff --git a/src/components/Header/index.tsx b/src/components/Header/index.tsx index ec6a8fb..1f269c7 100644 --- a/src/components/Header/index.tsx +++ b/src/components/Header/index.tsx @@ -22,7 +22,7 @@ const Header = () => { setTimeout(() => { history.push('/'); - }, 1000); + }, 200); }, [signOut, history]); const handleNavigateToHome = useCallback(() => { diff --git a/src/components/InputForm/index.tsx b/src/components/InputForm/index.tsx index 12f35d3..cc35ae3 100644 --- a/src/components/InputForm/index.tsx +++ b/src/components/InputForm/index.tsx @@ -1,10 +1,4 @@ -import React, { - InputHTMLAttributes, - useEffect, - useRef, - useState, - useCallback, -} from 'react'; +import React, { InputHTMLAttributes, useEffect, useRef, useState } from 'react'; import { FiAlertCircle } from 'react-icons/fi'; import { useField } from '@unform/core'; @@ -18,22 +12,10 @@ interface InputProps extends InputHTMLAttributes { const Input = ({ name, title, ...rest }: InputProps) => { const inputRef = useRef(null); - - const [isFocused, setIsFocused] = useState(false); - const [isFilled, setIsFilled] = useState(false); + const [inputError, setInputError] = useState(''); const { fieldName, error, defaultValue, registerField } = useField(name); - const handleInputFocus = useCallback(() => { - setIsFocused(true); - }, []); - - const handleInputBlur = useCallback(() => { - setIsFocused(false); - - setIsFilled(!!inputRef.current?.value); - }, []); - useEffect(() => { registerField({ name: fieldName, @@ -42,21 +24,24 @@ const Input = ({ name, title, ...rest }: InputProps) => { }); }, [fieldName, registerField]); + useEffect(() => { + setInputError(error); + }, [error]); + return ( - +

{title}

setInputError('')} defaultValue={defaultValue} ref={inputRef} {...rest} /> - {error && ( - + {inputError && ( + )} diff --git a/src/components/InputForm/styles.ts b/src/components/InputForm/styles.ts index 677d5cd..e6fc64f 100644 --- a/src/components/InputForm/styles.ts +++ b/src/components/InputForm/styles.ts @@ -34,11 +34,14 @@ export const Container = styled.div` background-color: var(--soft-gray); border-radius: 1.25rem; - border: 1px solid rgba(206, 207, 208, 0.2); + border: 2px solid rgba(206, 207, 208, 0.2); + + transition: border-color .3s; ${props => props.isErrored && css` + transition: border-color .3s; border-color: #c53030; `} @@ -61,8 +64,6 @@ export const Container = styled.div` color: #adadad; } } - - } `; @@ -72,14 +73,6 @@ export const Error = styled(Tooltip)` svg { margin: 0; - } - - span { - background: #c53030; - color: #fff; - - &::before { - border-color: #c53030 transparent; - } + cursor: info; } `; diff --git a/src/components/Tooltip/styles.ts b/src/components/Tooltip/styles.ts index 0402910..ecbd6c6 100644 --- a/src/components/Tooltip/styles.ts +++ b/src/components/Tooltip/styles.ts @@ -5,7 +5,6 @@ export const Container = styled.div` span { width: 160px; - background: #ff9000; padding: 8px; border-radius: 4px; font-size: 14px; @@ -19,12 +18,16 @@ export const Container = styled.div` left: 50%; transform: translateX(-50%); - color: #312e38; + display: flex; + justify-content: center; + + background-color: #c53030; + color: #ffffff; &::before { content: ''; border-style: solid; - border-color: #ff9000 transparent; + border-color: #c53030 transparent; border-width: 6px 6px 0 6px; bottom: 20px; top: 100%; diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx index 66b2e0e..f4d69c8 100644 --- a/src/contexts/AuthContext.tsx +++ b/src/contexts/AuthContext.tsx @@ -7,9 +7,11 @@ import { AuthContextData, AuthProviderProps, AuthState } from 'DTOs/Auth'; const AuthContext = createContext({} as AuthContextData); const AuthProvider = ({ children }: AuthProviderProps) => { - const [error, setError] = useState(''); - const [success, setSuccess] = useState(false); - const [loader, setLoader] = useState(false); + const [signUpError, setSignUpError] = useState(''); + const [signUpLoader, setSignUpLoader] = useState(false); + + const [inviteError, setInviteError] = useState(''); + const [inviteLoader, setInviteLoader] = useState(false); const [data, setData] = useState(() => { const token = localStorage.getItem('@Typext:token'); @@ -26,33 +28,53 @@ const AuthProvider = ({ children }: AuthProviderProps) => { }); const signIn = useCallback(async ({ email, password }) => { - const response = await api.post('sessions', { - email, - password, - }); + try { + const response = await api.post('sessions', { + email, + password, + }); + + const { token, user } = response.data; - const { token, user } = response.data; + localStorage.setItem('@Typext:token', token); + localStorage.setItem('@Typext:user', JSON.stringify(user)); - localStorage.setItem('@Typext:token', token); - localStorage.setItem('@Typext:user', JSON.stringify(user)); + setData({ token, user }); + return true; + } catch (err) { + const errorStatus = err.response?.status; + + if (errorStatus === 401) { + setInviteError(err.response?.data.message); + } - setData({ token, user }); + return false; + } }, []); const signUp = useCallback(async credentials => { try { - const response = await api.post('/users', credentials); + setSignUpError(''); + + setSignUpLoader(true); + const { email, password } = credentials; + + await api.post('/users', credentials); + + const returnData = { + email, + password, + }; - setSuccess(true); - localStorage.setItem('@Typext:user', JSON.stringify(response.data)); - localStorage.setItem('@Typext:token', credentials.token); + setSignUpLoader(false); + return returnData; } catch (err) { const errorStatus = err.response?.status; - if (errorStatus === 401) { - setError(err.response?.data.message); - setSuccess(false); - } + setSignUpError(errorStatus); + setSignUpLoader(false); + + return null; } }, []); @@ -64,9 +86,10 @@ const AuthProvider = ({ children }: AuthProviderProps) => { }, []); const inviteUser = useCallback(async ({ name, email, type }) => { - setLoader(true); + setInviteLoader(true); try { + setInviteError(''); const response = await api.post('/invite-users', { name, email, @@ -79,18 +102,15 @@ const AuthProvider = ({ children }: AuthProviderProps) => { '@Typext:invite_data', JSON.stringify({ name: inviteData.name, email: inviteData.email }), ); - - setSuccess(true); } catch (err) { const errorStatus = err.response?.status; if (errorStatus === 401) { - setError(err.response?.data.message); - setSuccess(false); + setInviteError(err.response?.data.message); } } - setLoader(false); + setInviteLoader(false); }, []); return ( @@ -98,14 +118,12 @@ const AuthProvider = ({ children }: AuthProviderProps) => { value={{ user: data.user, invitation: { - error, - success, - loader, + error: inviteError, + loader: inviteLoader, }, register: { - error, - success, - loader, + error: signUpError, + loader: signUpLoader, }, signIn, signUp, diff --git a/src/pages/InviteUsers/components/InviteConfirmationModal/index.tsx b/src/pages/InviteUsers/components/InviteConfirmationModal/index.tsx index ecd2a85..1586f58 100644 --- a/src/pages/InviteUsers/components/InviteConfirmationModal/index.tsx +++ b/src/pages/InviteUsers/components/InviteConfirmationModal/index.tsx @@ -15,14 +15,16 @@ interface InviteConfirmationProps { } const InviteConfirmationModal = ({ onClose }: InviteConfirmationProps) => { - const { invitation } = useAuth(); + const { + invitation: { error, loader }, + } = useAuth(); return ( - {invitation.loader ? ( + {loader ? ( - ) : invitation.success ? ( + ) : error === '' ? (

E-MAIL ENVIADO!

@@ -32,7 +34,7 @@ const InviteConfirmationModal = ({ onClose }: InviteConfirmationProps) => {

ERRO AO ENVIAR CONVITE

-

{invitation.error?.toUpperCase()}

+

{error?.toUpperCase()}

)}
diff --git a/src/pages/Login/index.tsx b/src/pages/Login/index.tsx index bf686f4..04f2b02 100644 --- a/src/pages/Login/index.tsx +++ b/src/pages/Login/index.tsx @@ -1,60 +1,109 @@ -import React from 'react'; +import React, { useRef, useCallback } from 'react'; +import { useHistory } from 'react-router-dom'; -import Input from 'components/Input/Input'; +import { Form } from '@unform/web'; +import { FormHandles } from '@unform/core'; + +import { useAuth } from 'contexts/AuthContext'; +import getValidationErrors from 'utils/getValidationErrors'; + +import InputForm from 'components/InputForm'; import Button from 'components/Button/Button'; import Logo from 'assets/logo.svg'; + +import loginSchema from './loginSchema'; import Content from './styles'; +interface SignInData { + email: string; + password: string; +} + function Login() { - return ( - <> - + const { signIn } = useAuth(); + const history = useHistory(); + const formRef = useRef(null); -
+ const handleLoginDebug = useCallback(() => { + const debugUser = { + name: 'Debug Develop', + email: 'debug@email.com', + office: 'Dev', + area: 'TI', + company: 'typext', + phone: '12 99999999', + type: 'Admin', + active: true, + created_at: '2021-04-05T11:30:46.204Z', + updated_at: '2021-04-05T11:33:32.689Z', + }; - - Logo - + localStorage.setItem('@Typext:user', JSON.stringify(debugUser)); + localStorage.setItem( + '@Typext:token', + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2MTc2MjI2MTgsImV4cCI6MTYxNzcwOTAxOCwic3ViIjoiMmNmNjMzYTEtNTlhZS00YWU5LWJmODYtNzhjY2RhOWUxZGYyIn0.3a1udwBE93FOAB7guxHLa5WKB-_Ehmk4JO35UqtKrj4', + ); + history.push('/home'); + }, [history]); + + const handleLogin = useCallback( + (data: SignInData) => { + const isLoginSuccess = signIn(data); -
- + if (isLoginSuccess) history.push('/home'); + }, + [signIn, history], + ); - + const handleSubmit = useCallback( + async (data: SignInData) => { + if (data.email === 'debug@email.com') { + handleLoginDebug(); + return; + } -
+ try { + formRef.current?.setErrors({}); -
- + handleLogin(data); + } catch (err) { + const errors = getValidationErrors(err); + formRef.current?.setErrors(errors); + } + }, + [handleLogin, handleLoginDebug], + ); - - Esqueceu a senha? - Clique aqui. - + return ( + <> + +
+ + Logo + -
+
+ +
- - - ); + + + + + ); } export default Login; diff --git a/src/pages/Login/loginSchema.ts b/src/pages/Login/loginSchema.ts new file mode 100644 index 0000000..e05965e --- /dev/null +++ b/src/pages/Login/loginSchema.ts @@ -0,0 +1,10 @@ +import * as Yup from 'yup'; + +const schema = Yup.object().shape({ + email: Yup.string() + .required('O email é obrigatório') + .email('Digite um email valído'), + password: Yup.string().required('A senha é obrigatória'), +}); + +export default schema; diff --git a/src/pages/Login/styles.ts b/src/pages/Login/styles.ts index 0a088c6..c612434 100644 --- a/src/pages/Login/styles.ts +++ b/src/pages/Login/styles.ts @@ -4,6 +4,8 @@ const Content = styled.div` display: flex; justify-content: center; + width: 100%; + height: 100%; padding: 30px; .Login { @@ -36,7 +38,7 @@ const Content = styled.div` flex-direction: column; align-items: flex-start; - Input { + > div { margin-bottom: 2rem; } } @@ -70,8 +72,6 @@ const Content = styled.div` @media (max-width: 1024px) { align-items: center; - width: 100%; - height: 100%; margin: 0; } diff --git a/src/pages/RegisterNewUser/components/RegisterModal/index.tsx b/src/pages/RegisterNewUser/components/RegisterModal/index.tsx index ac725f0..c672e6e 100644 --- a/src/pages/RegisterNewUser/components/RegisterModal/index.tsx +++ b/src/pages/RegisterNewUser/components/RegisterModal/index.tsx @@ -1,5 +1,4 @@ -import React, { useEffect } from 'react'; -import { useHistory } from 'react-router-dom'; +import React from 'react'; import { useAuth } from 'contexts'; @@ -17,24 +16,15 @@ interface RegisterModalProps { const RegisterModal = ({ onClose }: RegisterModalProps) => { const { - register: { success, loader, error }, + register: { loader, error }, } = useAuth(); - const history = useHistory(); - - useEffect(() => { - if (success) { - setTimeout(() => { - history.push('/home'); - }, 2000); - } - }, [success, history]); return ( {loader ? ( - ) : success ? ( + ) : error === '' ? (

CADASTRADO!

diff --git a/src/pages/RegisterNewUser/index.tsx b/src/pages/RegisterNewUser/index.tsx index a9d20a8..2837d7a 100644 --- a/src/pages/RegisterNewUser/index.tsx +++ b/src/pages/RegisterNewUser/index.tsx @@ -96,11 +96,12 @@ const RegisterNewUser = () => { - +
diff --git a/src/services/api.ts b/src/services/api.ts index 4416b7e..8d5bb2b 100644 --- a/src/services/api.ts +++ b/src/services/api.ts @@ -1,7 +1,13 @@ import axios from 'axios'; +import { getUserToken } from './auth'; + +const token = getUserToken(); const api = axios.create({ baseURL: 'http://localhost:3333', + headers: { + Authorization: `token ${token}`, + }, }); export default api; diff --git a/src/services/auth.ts b/src/services/auth.ts index 5267f6f..ddace12 100644 --- a/src/services/auth.ts +++ b/src/services/auth.ts @@ -27,3 +27,9 @@ export function getInviteInfo(): UserInfo { return userData; } + +export function getUserToken() { + const token = localStorage.getItem('@Typext:token'); + + return token; +}