diff --git a/src/App.tsx b/src/App.tsx index 6edfc25..95972e6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -12,6 +12,8 @@ import Loans from "./pages/Loans" import { Toaster } from "./components/ui/toaster" import PrivateRoute from './PrivateRoute'; import SignIn from "./pages/SignIn" +import RecoverPassword from "./pages/RecoverPassword" +import ChangePassword from "./pages/ChangePassword" function App() { return ( @@ -22,6 +24,8 @@ function App() { } /> } /> } /> + } /> + } /> } /> } /> } /> diff --git a/src/hooks/useApi/index.tsx b/src/hooks/useApi/index.tsx index 15a2712..f7412f5 100644 --- a/src/hooks/useApi/index.tsx +++ b/src/hooks/useApi/index.tsx @@ -71,6 +71,26 @@ const useApi = () => { .catch((err) => resolve(getDefaultErrorUseAPIMessage(err))); }); }, + recoverPassword: (email: string): Promise<{ data: any }> => { + return new Promise((resolve) => { + api + .post(`/auth/recover-password/${email}`) + .then((res) => resolve(res)) + .catch((err) => resolve(getDefaultErrorUseAPIMessage(err))); + }); + }, + changePassword: (password: string, token: string): Promise<{ data: any }> => { + return new Promise((resolve) => { + api + .post(`/auth/change-password`, { password }, { + headers: { + Authorization: `Bearer ${token}`, + }, + }) + .then((res) => resolve(res)) + .catch((err) => resolve(getDefaultErrorUseAPIMessage(err))); + }); + }, editProfile: async (id: string, data: { firstName: string; lastName: string; diff --git a/src/hooks/useAuth.tsx b/src/hooks/useAuth.tsx index e2628c7..9c5c60d 100644 --- a/src/hooks/useAuth.tsx +++ b/src/hooks/useAuth.tsx @@ -34,6 +34,8 @@ type AuthContextType = { signUp: (userToSignUp: SignUpParams) => Promise; signIn: (userToSignIn: SignInParams) => Promise; editProfile: (id: string, profileToEdit: EditProfileParams) => Promise; + recoverPassword: (email: string) => Promise; + changePassword: (password: string, token: string) => Promise; }; const AuthContext = createContext({} as AuthContextType); @@ -43,6 +45,8 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { signUp: authSignUp, signIn: authSignIn, editProfile: authEditProfile, + recoverPassword: authRecoverPassword, + changePassword: authChangePassword, } = useApi(); const localToken = @@ -87,6 +91,40 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { return true; } + async function recoverPassword(email: string): Promise { + const { data } = await authRecoverPassword(email); + if (data.id) { + toaster.create({ + title: 'E-mail enviado para recuperação de senha!', + type: 'success', + }) + return true; + }; + toaster.create({ + title: 'Erro ao requisitar recuperação de senha', + description: 'Verifique os campos e tente novamente.', + type: 'error', + }) + return false; + } + + async function changePassword(password: string, token: string): Promise { + const { data } = await authChangePassword(password, token); + if (data.id) { + toaster.create({ + title: 'Senha alterada com sucesso! Você será redirecionado para o login...', + type: 'success', + }) + return true; + }; + toaster.create({ + title: 'Erro ao alterar a senha', + description: 'Verifique os campos e tente novamente.', + type: 'error', + }) + return false; + } + async function editProfile(id: string, profileToEdit: EditProfileParams): Promise { const { data } = await authEditProfile(id, profileToEdit); if (data.id) { @@ -120,6 +158,8 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { signUp, editProfile, signIn, + recoverPassword, + changePassword, }} > {children} diff --git a/src/pages/ChangePassword/ChangePasswordForm/index.tsx b/src/pages/ChangePassword/ChangePasswordForm/index.tsx new file mode 100644 index 0000000..70a847c --- /dev/null +++ b/src/pages/ChangePassword/ChangePasswordForm/index.tsx @@ -0,0 +1,75 @@ +import { useState } from 'react'; +import { useAuth } from '../../../hooks/useAuth'; +import { Stack } from '@chakra-ui/react'; +import { useForm } from 'react-hook-form'; +import { Button } from '../../../components/ui/button'; +import { Field } from '../../../components/ui/field'; +import { PasswordInput } from '../../../components/ui/password-input'; +import { useLocation } from 'react-router'; + +interface FormValues { + password: string; + passwordConfirmation: string; +} + +function useQuery() { + return new URLSearchParams(useLocation().search); +} + +function ChangePasswordForm() { + const query = useQuery(); + const [loading, setLoading] = useState(false); + + const token = query.get('token'); + + const { + register, + handleSubmit, + formState: { errors, isValid }, + } = useForm(); + + const { changePassword } = useAuth(); + + const onSubmit = handleSubmit(async (data: FormValues) => { + if (!token) return; + setLoading(true); + await changePassword(data.password, token); + setLoading(false); + }) + + return ( +
+ + + + + + + + + + + +
+ ); +} + +export default ChangePasswordForm diff --git a/src/pages/ChangePassword/ChangePasswordHeader/index.tsx b/src/pages/ChangePassword/ChangePasswordHeader/index.tsx new file mode 100644 index 0000000..4a84929 --- /dev/null +++ b/src/pages/ChangePassword/ChangePasswordHeader/index.tsx @@ -0,0 +1,12 @@ +import { Stack, Text } from '@chakra-ui/react'; + +function RecoverPasswordHeader() { + return ( + + Nova senha + Deseja criar uma nova senha? Insira sua senha e confirme-a. + + ); +} + +export default RecoverPasswordHeader diff --git a/src/pages/ChangePassword/index.tsx b/src/pages/ChangePassword/index.tsx new file mode 100644 index 0000000..af45e53 --- /dev/null +++ b/src/pages/ChangePassword/index.tsx @@ -0,0 +1,18 @@ +import { Box, Center, Stack } from '@chakra-ui/react'; +import ChangePasswordForm from './ChangePasswordForm'; +import ChangePasswordHeader from './ChangePasswordHeader'; + +function ChangePassword() { + return ( + +
+ + + + +
+
+ ); +} + +export default ChangePassword diff --git a/src/pages/RecoverPassword/RecoverPasswordForm/index.tsx b/src/pages/RecoverPassword/RecoverPasswordForm/index.tsx new file mode 100644 index 0000000..91dfd4b --- /dev/null +++ b/src/pages/RecoverPassword/RecoverPasswordForm/index.tsx @@ -0,0 +1,63 @@ +import { useState } from 'react'; +import { useAuth } from '../../../hooks/useAuth'; +import { Input, Stack } from '@chakra-ui/react'; +import { useForm } from 'react-hook-form'; +import { Button } from '../../../components/ui/button'; +import { Field } from '../../../components/ui/field'; + +interface FormValues { + email: string; +} + +function RecoverPasswordForm() { + const [loading, setLoading] = useState(false); + + const { + register, + handleSubmit, + formState: { errors, isValid }, + } = useForm(); + + const { recoverPassword } = useAuth(); + + const onSubmit = handleSubmit(async (data: FormValues) => { + setLoading(true); + await recoverPassword(data.email); + setLoading(false); + }) + + return ( +
+ + + + + + + + +
+ ); +} + +export default RecoverPasswordForm diff --git a/src/pages/RecoverPassword/RecoverPasswordHeader/index.tsx b/src/pages/RecoverPassword/RecoverPasswordHeader/index.tsx new file mode 100644 index 0000000..b6f8a9e --- /dev/null +++ b/src/pages/RecoverPassword/RecoverPasswordHeader/index.tsx @@ -0,0 +1,12 @@ +import { Stack, Text } from '@chakra-ui/react'; + +function RecoverPasswordHeader() { + return ( + + Recuperação de Senha + Para recuperar o acesso a sua conta, vamos enviar um código para seu e-mail. + + ); +} + +export default RecoverPasswordHeader diff --git a/src/pages/RecoverPassword/index.tsx b/src/pages/RecoverPassword/index.tsx new file mode 100644 index 0000000..7ee1054 --- /dev/null +++ b/src/pages/RecoverPassword/index.tsx @@ -0,0 +1,18 @@ +import { Box, Center, Stack } from '@chakra-ui/react'; +import RecoverPasswordForm from './RecoverPasswordForm'; +import RecoverPasswordHeader from './RecoverPasswordHeader'; + +function RecoverPassword() { + return ( + +
+ + + + +
+
+ ); +} + +export default RecoverPassword