diff --git a/public/locales/bg/auth.json b/public/locales/bg/auth.json index b75ffd2b3..4885b8de7 100644 --- a/public/locales/bg/auth.json +++ b/public/locales/bg/auth.json @@ -2,7 +2,11 @@ "alerts": { "welcome": "Добре дошли!", "invalid-login": "Грешен потребител / парола.", - "re-login": "Моля, влезте отново в профила си." + "re-login": "Моля, влезте отново в профила си.", + "forgotten-password-error": "Потребителя не е намерен, моля опитайте отново!", + "forgotten-password-success": "Моля проверете имейла си!", + "change-password-error": "Възникна грешка, моля опитайте отново или се свържете с info@podkrepi.bg!", + "change-password-success": "Може да се логнете с новата парола." }, "cta": { "login": "Вход", @@ -28,6 +32,7 @@ }, "account": { "email": "Email", + "forgotten-password": "Забравена парола", "new-password": "Нова парола", "confirm-password": "Потвърди парола", "previous-password": "Стара парола", diff --git a/public/locales/en/auth.json b/public/locales/en/auth.json index da6ca75d2..88246118b 100644 --- a/public/locales/en/auth.json +++ b/public/locales/en/auth.json @@ -2,7 +2,11 @@ "alerts": { "welcome": "Welcome!", "invalid-login": "Wrong username / password.", - "re-login": "Please login into your account." + "re-login": "Please login into your account.", + "forgotten-password-error": "User not found, please try again!", + "forgotten-password-success": "Please check your email!", + "change-password-error": " Тhere was an error, try again or contact info@podkrepi.bg!", + "change-password-success": "You can log in with the new password." }, "cta": { "login": "Login", @@ -28,6 +32,7 @@ }, "account": { "email": "Email", + "forgotten-password": "Forgotten password", "new-password": "New password", "confirm-password": "Confirm password", "previous-password": "Previous password", diff --git a/src/common/util/useCurrentPerson.ts b/src/common/util/useCurrentPerson.ts index 5f02be423..69c4d965a 100644 --- a/src/common/util/useCurrentPerson.ts +++ b/src/common/util/useCurrentPerson.ts @@ -7,6 +7,8 @@ import { endpoints } from 'service/apiEndpoints' import { Person, UpdatePerson } from 'gql/person' import { authConfig, authQueryFnFactory } from 'service/restRequests' import { Credentials } from 'components/auth/profile/UpdatePasswordModal' +import { ForgottenPasswordForm } from 'components/auth/forgottenPassword/ForgottenPasswordForm' +import { ChangePasswordFormData } from 'components/auth/changePassword/ChangePasswordForm' type CurrentPerson = { user: Person @@ -51,6 +53,24 @@ export function updateCurrentPersonPassword() { } } +export function forgottenPassword() { + return async (data: ForgottenPasswordForm) => { + return await apiClient.post>( + endpoints.account.forgottenPassword.url, + data, + ) + } +} + +export function resetPassword() { + return async (data: ChangePasswordFormData) => { + return await apiClient.post>( + endpoints.account.resetPassword.url, + data, + ) + } +} + export function disableCurrentPerson() { const { data: session } = useSession() return async () => { diff --git a/src/components/auth/changePassword/ChangePasswordForm.tsx b/src/components/auth/changePassword/ChangePasswordForm.tsx index d4bfa0ad9..7f76ecb93 100644 --- a/src/components/auth/changePassword/ChangePasswordForm.tsx +++ b/src/components/auth/changePassword/ChangePasswordForm.tsx @@ -1,15 +1,24 @@ -import React from 'react' +import React, { useState } from 'react' import * as yup from 'yup' import { Grid } from '@mui/material' import { customValidators } from 'common/form/useForm' import SubmitButton from 'components/common/form/SubmitButton' import GenericForm from 'components/common/form/GenericForm' -import FormTextField from 'components/common/form/FormTextField' +import { routes } from 'common/routes' +import { useRouter } from 'next/router' +import { useMutation } from 'react-query' +import { AxiosError, AxiosResponse } from 'axios' +import { ApiErrors } from 'service/apiErrors' +import PasswordField from 'components/common/form/PasswordField' +import { resetPassword } from 'common/util/useCurrentPerson' +import { AlertStore } from 'stores/AlertStore' +import { useTranslation } from 'next-i18next' export type ChangePasswordFormData = { password: string confirmPassword: string + token?: string | string[] } const validationSchema: yup.SchemaOf = yup @@ -22,11 +31,13 @@ const validationSchema: yup.SchemaOf = yup .min(6, customValidators.passwordMin) .required() .oneOf([yup.ref('password'), null], 'validation:password-match'), + token: yup.string(), }) const defaults: ChangePasswordFormData = { password: '', confirmPassword: '', + token: '', } export type ChangePasswordFormProps = { @@ -34,8 +45,31 @@ export type ChangePasswordFormProps = { } export default function ChangePasswordForm({ initialValues = defaults }: ChangePasswordFormProps) { - const onSubmit = (values: ChangePasswordFormData) => { - console.log(values) + const [loading, setLoading] = useState(false) + const router = useRouter() + const token = router.query.token + const { t } = useTranslation() + + const mutation = useMutation, ChangePasswordFormData>({ + mutationFn: resetPassword(), + onError: () => AlertStore.show(t('auth:alerts.change-password-error'), 'error'), + onSuccess: () => AlertStore.show(t('auth:alerts.change-password-success'), 'success'), + }) + const onSubmit = async (values: ChangePasswordFormData) => { + values.token = token + try { + setLoading(true) + const res = await mutation.mutateAsync(values) + console.log(res) + if (!res) { + throw new Error(res) + } + router.push(routes.login) + } catch (error) { + console.error(error) + } finally { + setLoading(false) + } } return ( @@ -45,17 +79,17 @@ export default function ChangePasswordForm({ initialValues = defaults }: ChangeP validationSchema={validationSchema}> - + - - + diff --git a/src/components/auth/forgottenPassword/ForgottenPasswordForm.tsx b/src/components/auth/forgottenPassword/ForgottenPasswordForm.tsx index 51255243d..6f5e015f3 100644 --- a/src/components/auth/forgottenPassword/ForgottenPasswordForm.tsx +++ b/src/components/auth/forgottenPassword/ForgottenPasswordForm.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useState } from 'react' import * as yup from 'yup' import { useTranslation } from 'next-i18next' import { Typography, Grid } from '@mui/material' @@ -6,6 +6,11 @@ import { Typography, Grid } from '@mui/material' import SubmitButton from 'components/common/form/SubmitButton' import GenericForm from 'components/common/form/GenericForm' import FormTextField from 'components/common/form/FormTextField' +import { useMutation } from 'react-query' +import { AxiosError, AxiosResponse } from 'axios' +import { ApiErrors } from 'service/apiErrors' +import { AlertStore } from 'stores/AlertStore' +import { forgottenPassword } from 'common/util/useCurrentPerson' export type ForgottenPasswordForm = { email: string @@ -27,9 +32,23 @@ export default function ForgottenPasswordForm({ initialValues = defaults, }: ForgottenPasswordFormProps) { const { t } = useTranslation() + const [loading, setLoading] = useState(false) - const onSubmit = (values: ForgottenPasswordForm) => { - console.log(values) + const mutation = useMutation, ForgottenPasswordForm>({ + mutationFn: forgottenPassword(), + onError: () => AlertStore.show(t('auth:alerts.forgotten-password-error'), 'error'), + onSuccess: () => AlertStore.show(t('auth:alerts.forgotten-password-success'), 'success'), + }) + + const onSubmit = async (values: ForgottenPasswordForm) => { + try { + setLoading(true) + await mutation.mutateAsync(values) + } catch (error) { + console.error(error) + } finally { + setLoading(false) + } } return ( @@ -45,7 +64,7 @@ export default function ForgottenPasswordForm({ - + diff --git a/src/components/auth/login/LoginForm.tsx b/src/components/auth/login/LoginForm.tsx index 1dde46480..4e3ae22e6 100644 --- a/src/components/auth/login/LoginForm.tsx +++ b/src/components/auth/login/LoginForm.tsx @@ -14,6 +14,7 @@ import FormTextField from 'components/common/form/FormTextField' import Google from 'common/icons/Google' import PasswordField from 'components/common/form/PasswordField' import { email, password } from 'common/form/validation' +import LinkButton from 'components/common/LinkButton' export type LoginFormData = { email: string @@ -78,6 +79,11 @@ export default function LoginForm({ initialValues = defaults }: LoginFormProps) + + + {t('auth:account.forgotten-password')} + + diff --git a/src/service/apiEndpoints.ts b/src/service/apiEndpoints.ts index ec2b96fce..f820469cc 100644 --- a/src/service/apiEndpoints.ts +++ b/src/service/apiEndpoints.ts @@ -167,6 +167,8 @@ export const endpoints = { me: { url: '/account/me', method: 'GET' }, update: { url: '/account/me', method: 'PATCH' }, updatePassword: { url: '/account/me/credentials', method: 'PATCH' }, + forgottenPassword: { url: '/login/forgot-password', method: 'POST' }, + resetPassword: { url: '/login/reset-password', method: 'POST' }, delete: { url: '/account/me', method: 'DELETE' }, new: { url: '/account/new', method: 'GET' }, },