Skip to content

Commit

Permalink
Implement forgotten password funcitonality (#997)
Browse files Browse the repository at this point in the history
* forgotten password

* change password

* display link in login form that redirect to forgotten password

* changed API endpoint

* translation
  • Loading branch information
borislavstoychev authored Aug 14, 2022
1 parent c9a0419 commit 31fbc63
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 13 deletions.
7 changes: 6 additions & 1 deletion public/locales/bg/auth.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
"alerts": {
"welcome": "Добре дошли!",
"invalid-login": "Грешен потребител / парола.",
"re-login": "Моля, влезте отново в профила си."
"re-login": "Моля, влезте отново в профила си.",
"forgotten-password-error": "Потребителя не е намерен, моля опитайте отново!",
"forgotten-password-success": "Моля проверете имейла си!",
"change-password-error": "Възникна грешка, моля опитайте отново или се свържете с [email protected]!",
"change-password-success": "Може да се логнете с новата парола."
},
"cta": {
"login": "Вход",
Expand All @@ -28,6 +32,7 @@
},
"account": {
"email": "Email",
"forgotten-password": "Забравена парола",
"new-password": "Нова парола",
"confirm-password": "Потвърди парола",
"previous-password": "Стара парола",
Expand Down
7 changes: 6 additions & 1 deletion public/locales/en/auth.json
Original file line number Diff line number Diff line change
Expand Up @@ -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 [email protected]!",
"change-password-success": "You can log in with the new password."
},
"cta": {
"login": "Login",
Expand All @@ -28,6 +32,7 @@
},
"account": {
"email": "Email",
"forgotten-password": "Forgotten password",
"new-password": "New password",
"confirm-password": "Confirm password",
"previous-password": "Previous password",
Expand Down
20 changes: 20 additions & 0 deletions src/common/util/useCurrentPerson.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -51,6 +53,24 @@ export function updateCurrentPersonPassword() {
}
}

export function forgottenPassword() {
return async (data: ForgottenPasswordForm) => {
return await apiClient.post<ForgottenPasswordForm, AxiosResponse<boolean>>(
endpoints.account.forgottenPassword.url,
data,
)
}
}

export function resetPassword() {
return async (data: ChangePasswordFormData) => {
return await apiClient.post<ChangePasswordFormData, AxiosResponse<boolean>>(
endpoints.account.resetPassword.url,
data,
)
}
}

export function disableCurrentPerson() {
const { data: session } = useSession()
return async () => {
Expand Down
48 changes: 41 additions & 7 deletions src/components/auth/changePassword/ChangePasswordForm.tsx
Original file line number Diff line number Diff line change
@@ -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<ChangePasswordFormData> = yup
Expand All @@ -22,20 +31,45 @@ const validationSchema: yup.SchemaOf<ChangePasswordFormData> = 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 = {
initialValues?: ChangePasswordFormData
}

export default function ChangePasswordForm({ initialValues = defaults }: ChangePasswordFormProps) {
const onSubmit = (values: ChangePasswordFormData) => {
console.log(values)
const [loading, setLoading] = useState<boolean>(false)
const router = useRouter()
const token = router.query.token
const { t } = useTranslation()

const mutation = useMutation<AxiosResponse, AxiosError<ApiErrors>, 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 (
Expand All @@ -45,17 +79,17 @@ export default function ChangePasswordForm({ initialValues = defaults }: ChangeP
validationSchema={validationSchema}>
<Grid container spacing={3}>
<Grid item xs={12}>
<FormTextField type="password" label="auth:fields.password" name="password" />
<PasswordField type="password" label="auth:fields.password" name="password" />
</Grid>
<Grid item xs={12}>
<FormTextField
<PasswordField
type="password"
label="auth:fields.confirm-password"
name="confirmPassword"
/>
</Grid>
<Grid item xs={12}>
<SubmitButton fullWidth label="auth:cta.reset" />
<SubmitButton loading={loading} fullWidth label="auth:cta.reset" />
</Grid>
</Grid>
</GenericForm>
Expand Down
27 changes: 23 additions & 4 deletions src/components/auth/forgottenPassword/ForgottenPasswordForm.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
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'

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
Expand All @@ -27,9 +32,23 @@ export default function ForgottenPasswordForm({
initialValues = defaults,
}: ForgottenPasswordFormProps) {
const { t } = useTranslation()
const [loading, setLoading] = useState<boolean>(false)

const onSubmit = (values: ForgottenPasswordForm) => {
console.log(values)
const mutation = useMutation<AxiosResponse, AxiosError<ApiErrors>, 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 (
Expand All @@ -45,7 +64,7 @@ export default function ForgottenPasswordForm({
<FormTextField type="text" label="auth:fields.email" name="email" />
</Grid>
<Grid item xs={12}>
<SubmitButton fullWidth label="auth:cta.send" />
<SubmitButton loading={loading} fullWidth label="auth:cta.send" />
</Grid>
</Grid>
</GenericForm>
Expand Down
6 changes: 6 additions & 0 deletions src/components/auth/login/LoginForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -78,6 +79,11 @@ export default function LoginForm({ initialValues = defaults }: LoginFormProps)
<Grid item xs={12}>
<PasswordField />
</Grid>
<Grid container justifyContent="flex-end">
<LinkButton href={routes.forgottenPassword}>
{t('auth:account.forgotten-password')}
</LinkButton>
</Grid>
<Grid item xs={12}>
<SubmitButton fullWidth label="auth:cta.login" loading={loading} />
</Grid>
Expand Down
2 changes: 2 additions & 0 deletions src/service/apiEndpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ export const endpoints = {
me: <Endpoint>{ url: '/account/me', method: 'GET' },
update: <Endpoint>{ url: '/account/me', method: 'PATCH' },
updatePassword: <Endpoint>{ url: '/account/me/credentials', method: 'PATCH' },
forgottenPassword: <Endpoint>{ url: '/login/forgot-password', method: 'POST' },
resetPassword: <Endpoint>{ url: '/login/reset-password', method: 'POST' },
delete: <Endpoint>{ url: '/account/me', method: 'DELETE' },
new: <Endpoint>{ url: '/account/new', method: 'GET' },
},
Expand Down

0 comments on commit 31fbc63

Please sign in to comment.