From e2ad94f80312ab170df4a7c330b671fc1415cd88 Mon Sep 17 00:00:00 2001 From: nadezhdakharchuk Date: Mon, 10 Jan 2022 10:42:39 +0300 Subject: [PATCH 1/5] add newPassword page --- .../update/useUpdatePasswordMutation.ts | 22 ++++++ api/types/user/updatePasswordApiType.ts | 8 ++ .../pages/newPassword/NewPasswordPage.jsx | 41 ++++++++++ .../NewPasswordForm/NewPasswordForm.tsx | 78 +++++++++++++++++++ .../components/NewPasswordForm/index.ts | 1 + .../components/NewPasswordForm/styled.ts | 13 ++++ components/pages/newPassword/index.ts | 1 + components/pages/newPassword/styled.ts | 6 ++ config/routes.ts | 1 + graphql/mutations/updatePassword.graphql | 5 ++ lib/apollo/hooks/actions/useUpdatePassword.ts | 39 ++++++++++ pages/new-password.ts | 1 + 12 files changed, 216 insertions(+) create mode 100644 api/mutations/update/useUpdatePasswordMutation.ts create mode 100644 api/types/user/updatePasswordApiType.ts create mode 100644 components/pages/newPassword/NewPasswordPage.jsx create mode 100644 components/pages/newPassword/components/NewPasswordForm/NewPasswordForm.tsx create mode 100644 components/pages/newPassword/components/NewPasswordForm/index.ts create mode 100644 components/pages/newPassword/components/NewPasswordForm/styled.ts create mode 100644 components/pages/newPassword/index.ts create mode 100644 components/pages/newPassword/styled.ts create mode 100644 graphql/mutations/updatePassword.graphql create mode 100644 lib/apollo/hooks/actions/useUpdatePassword.ts create mode 100644 pages/new-password.ts diff --git a/api/mutations/update/useUpdatePasswordMutation.ts b/api/mutations/update/useUpdatePasswordMutation.ts new file mode 100644 index 00000000..e1f4af1b --- /dev/null +++ b/api/mutations/update/useUpdatePasswordMutation.ts @@ -0,0 +1,22 @@ +import type { MutationHookOptions, MutationTuple } from '@apollo/client'; + +import UpdatePasswordMutation from 'graphql/mutations/updatePassword.graphql'; + +import type { UpdatePasswordVariables, UpdatePasswordData } from '../../types/user/updatePasswordApiType'; +import useMutation from '../../hooks/useMutationHook'; + +type UpdatePasswordResponseData = { + updatePassword: UpdatePasswordData; +}; + +type UpdatePasswordRequestVariables = { + input: UpdatePasswordVariables; +}; + +const useUpdatePasswordMutation = ( + options?: MutationHookOptions, +): MutationTuple => { + return useMutation(UpdatePasswordMutation, options); +}; + +export default useUpdatePasswordMutation; diff --git a/api/types/user/updatePasswordApiType.ts b/api/types/user/updatePasswordApiType.ts new file mode 100644 index 00000000..61c91e74 --- /dev/null +++ b/api/types/user/updatePasswordApiType.ts @@ -0,0 +1,8 @@ +export type UpdatePasswordData = { + accessToken: string; +}; + +export type UpdatePasswordVariables = { + password: string; + resetToken: string | string[] | undefined; +}; diff --git a/components/pages/newPassword/NewPasswordPage.jsx b/components/pages/newPassword/NewPasswordPage.jsx new file mode 100644 index 00000000..b4c20344 --- /dev/null +++ b/components/pages/newPassword/NewPasswordPage.jsx @@ -0,0 +1,41 @@ +import React from 'react'; +import Router from 'next/router'; + +import { withApolloClient } from 'lib/withApolloClient'; +import WithAuth from 'lib/auth/withAuth'; + +import { HOME } from 'config/routes'; +import { NotifierProvider } from 'contexts/NotifierContext'; + +import DefaultTemplate from 'components/shared/templates/DefaultTemplate'; +import Notifier from 'components/shared/atoms/Notifier'; + +import NewPasswordForm from './components/NewPasswordForm'; + +import { PageContentWrapper } from './styled'; + +const NewPasswordPage = ({ query }) => { + return ( + + + + + + + + + ); +}; + +NewPasswordPage.getInitialProps = ({ res, accessTokenManager }) => { + if (accessTokenManager.accessToken) { + if (res) { + res.redirect(302, HOME); + } else { + Router.push(HOME); + } + } + return {}; +}; + +export default withApolloClient(WithAuth(NewPasswordPage)); diff --git a/components/pages/newPassword/components/NewPasswordForm/NewPasswordForm.tsx b/components/pages/newPassword/components/NewPasswordForm/NewPasswordForm.tsx new file mode 100644 index 00000000..a8ebca38 --- /dev/null +++ b/components/pages/newPassword/components/NewPasswordForm/NewPasswordForm.tsx @@ -0,0 +1,78 @@ +import React from 'react'; +import { Form, Formik, FormikProps } from 'formik'; +import * as Yup from 'yup'; +import { useRouter } from 'next/router'; + +import useUpdatePassword from 'lib/apollo/hooks/actions/useUpdatePassword'; + +import Button from 'components/shared/atoms/Button'; +import FormFieldInput from 'components/shared/atoms/FormField'; +import Loader from 'components/shared/atoms/Loader'; +import { FormFieldType } from 'types/formsType'; + +import { FieldWrapper, FormContentWrapper, SubmitButtonWrapper } from './styled'; + +const passwordRegularExp = /^(?=.*[0-9])(?=.*[A-Z])(?=.*[a-z])([0-9A-Za-z#$@&!?.*^{}<>;,)(~'"=_%+-]+)$/; + +const initialValues = { + password: '', + passwordConfirmation: '', +}; + +const SignInValidationSchema = Yup.object().shape({ + password: Yup.string() + .required('This field is required') + .trim() + .min(6, 'The minimum password length is 6 characters') + .matches(passwordRegularExp, 'Password must contain upper and lower case characters and numbers'), + passwordConfirmation: Yup.string() + .oneOf([Yup.ref('password')], 'Password does not match') + .required('This field is required'), +}); + +type ValuesFromFormik = { + password: string; + passwordConfirmation: string; +}; + +const SignInFormContent = ({ isSubmitting }: FormikProps) => ( + +
+ + + + + + + + + +
+
+); + +const NewPasswordForm = () => { + const { query } = useRouter(); + const { reset_token: resetToken } = query; + const [updatePassword, updatePasswordState] = useUpdatePassword(); + + const onSubmit = async ({ password }: { password: string }) => { + await updatePassword({ password, resetToken }); + }; + + return ( + <> + + {updatePasswordState.loading && Loading...} + + ); +}; + +export default NewPasswordForm; diff --git a/components/pages/newPassword/components/NewPasswordForm/index.ts b/components/pages/newPassword/components/NewPasswordForm/index.ts new file mode 100644 index 00000000..60ab94d1 --- /dev/null +++ b/components/pages/newPassword/components/NewPasswordForm/index.ts @@ -0,0 +1 @@ +export { default } from './NewPasswordForm'; diff --git a/components/pages/newPassword/components/NewPasswordForm/styled.ts b/components/pages/newPassword/components/NewPasswordForm/styled.ts new file mode 100644 index 00000000..4658fddd --- /dev/null +++ b/components/pages/newPassword/components/NewPasswordForm/styled.ts @@ -0,0 +1,13 @@ +import styled from 'styled-components'; + +export const FormContentWrapper = styled.div` + width: 40rem; +`; + +export const FieldWrapper = styled.div` + margin-top: 1rem; +`; + +export const SubmitButtonWrapper = styled.div` + margin-top: 2rem; +`; diff --git a/components/pages/newPassword/index.ts b/components/pages/newPassword/index.ts new file mode 100644 index 00000000..7529d38b --- /dev/null +++ b/components/pages/newPassword/index.ts @@ -0,0 +1 @@ +export { default } from './NewPasswordPage'; diff --git a/components/pages/newPassword/styled.ts b/components/pages/newPassword/styled.ts new file mode 100644 index 00000000..3a7d76a1 --- /dev/null +++ b/components/pages/newPassword/styled.ts @@ -0,0 +1,6 @@ +import styled from 'styled-components'; + +export const PageContentWrapper = styled.div` + display: flex; + justify-content: center; +`; diff --git a/config/routes.ts b/config/routes.ts index c807fb2c..5aaf5bf3 100644 --- a/config/routes.ts +++ b/config/routes.ts @@ -6,3 +6,4 @@ export const STATIC_PAGE = '/static-page'; export const SIGNIN = '/signin'; export const SIGNUP = '/signup'; export const RECOVERY_PASSWORD = '/recovery-password'; +export const NEW_PASSWORD = '/new-password'; diff --git a/graphql/mutations/updatePassword.graphql b/graphql/mutations/updatePassword.graphql new file mode 100644 index 00000000..16b681cc --- /dev/null +++ b/graphql/mutations/updatePassword.graphql @@ -0,0 +1,5 @@ +mutation UpdatePassword($input: UpdatePasswordInput!) { + updatePassword(input: $input) { + accessToken + } +} diff --git a/lib/apollo/hooks/actions/useUpdatePassword.ts b/lib/apollo/hooks/actions/useUpdatePassword.ts new file mode 100644 index 00000000..f4e188d8 --- /dev/null +++ b/lib/apollo/hooks/actions/useUpdatePassword.ts @@ -0,0 +1,39 @@ +import { useRouter } from 'next/router'; + +import { SIGNIN } from 'config/routes'; +import { useNotifier } from 'contexts/NotifierContext'; + +import useUpdatePasswordMutation from 'api/mutations/update/useUpdatePasswordMutation'; + +export type UpdatePasswordProps = { + password: string; + resetToken: string | string[] | undefined; +}; + +const useUpdatePassword = () => { + const { setError, setSuccess } = useNotifier(); + const router = useRouter(); + + const onCompleted = () => { + setSuccess('Пароль успешно изменен'); + setTimeout(() => router.push(SIGNIN), 1000); + }; + + const [mutation, mutationResult] = useUpdatePasswordMutation({ + onCompleted, + }); + + const mutate = async ({ password, resetToken }: UpdatePasswordProps) => { + const updatePasswordInput = { password, resetToken }; + + try { + await mutation({ variables: { input: updatePasswordInput } }); + } catch (error) { + if (setError) setError(error); + } + }; + + return [mutate, mutationResult] as const; +}; + +export default useUpdatePassword; diff --git a/pages/new-password.ts b/pages/new-password.ts new file mode 100644 index 00000000..0693350b --- /dev/null +++ b/pages/new-password.ts @@ -0,0 +1 @@ +export { default } from 'components/pages/newPassword'; From c01f3a90db9ca8df939b8a6ffa9dfdecf9489bc8 Mon Sep 17 00:00:00 2001 From: nadezhdakharchuk Date: Tue, 11 Jan 2022 11:44:25 +0300 Subject: [PATCH 2/5] apply review comment --- api/mutations/update/useUpdatePasswordMutation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/mutations/update/useUpdatePasswordMutation.ts b/api/mutations/update/useUpdatePasswordMutation.ts index e1f4af1b..647d28f8 100644 --- a/api/mutations/update/useUpdatePasswordMutation.ts +++ b/api/mutations/update/useUpdatePasswordMutation.ts @@ -14,7 +14,7 @@ type UpdatePasswordRequestVariables = { }; const useUpdatePasswordMutation = ( - options?: MutationHookOptions, + options: MutationHookOptions, ): MutationTuple => { return useMutation(UpdatePasswordMutation, options); }; From e9065e618d1322bcd9c7f8be429641b983d1d62f Mon Sep 17 00:00:00 2001 From: Diana Safyanova Date: Tue, 11 Jan 2022 17:07:45 +0300 Subject: [PATCH 3/5] [TS] small fix --- api/mutations/update/useUpdatePasswordMutation.ts | 12 ++++++++---- lib/apollo/hooks/actions/useUpdatePassword.ts | 13 +++++-------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/api/mutations/update/useUpdatePasswordMutation.ts b/api/mutations/update/useUpdatePasswordMutation.ts index 647d28f8..dc6bc472 100644 --- a/api/mutations/update/useUpdatePasswordMutation.ts +++ b/api/mutations/update/useUpdatePasswordMutation.ts @@ -1,4 +1,4 @@ -import type { MutationHookOptions, MutationTuple } from '@apollo/client'; +import type { MutationHookOptions, MutationTuple, MutationResult } from '@apollo/client'; import UpdatePasswordMutation from 'graphql/mutations/updatePassword.graphql'; @@ -13,9 +13,13 @@ type UpdatePasswordRequestVariables = { input: UpdatePasswordVariables; }; -const useUpdatePasswordMutation = ( - options: MutationHookOptions, -): MutationTuple => { +type UpdatePasswordMutationOptions = MutationHookOptions; + +type UpdatePasswordMutationTuple = MutationTuple; + +export type UpdatePasswordMutationResult = MutationResult; + +const useUpdatePasswordMutation = (options: UpdatePasswordMutationOptions): UpdatePasswordMutationTuple => { return useMutation(UpdatePasswordMutation, options); }; diff --git a/lib/apollo/hooks/actions/useUpdatePassword.ts b/lib/apollo/hooks/actions/useUpdatePassword.ts index f4e188d8..d0aecc5f 100644 --- a/lib/apollo/hooks/actions/useUpdatePassword.ts +++ b/lib/apollo/hooks/actions/useUpdatePassword.ts @@ -3,14 +3,11 @@ import { useRouter } from 'next/router'; import { SIGNIN } from 'config/routes'; import { useNotifier } from 'contexts/NotifierContext'; +import type { UpdatePasswordVariables } from 'api/types/user/updatePasswordApiType'; +import type { UpdatePasswordMutationResult } from 'api/mutations/update/useUpdatePasswordMutation'; import useUpdatePasswordMutation from 'api/mutations/update/useUpdatePasswordMutation'; -export type UpdatePasswordProps = { - password: string; - resetToken: string | string[] | undefined; -}; - -const useUpdatePassword = () => { +const useUpdatePassword = (): [(variables: UpdatePasswordVariables) => Promise, UpdatePasswordMutationResult] => { const { setError, setSuccess } = useNotifier(); const router = useRouter(); @@ -23,7 +20,7 @@ const useUpdatePassword = () => { onCompleted, }); - const mutate = async ({ password, resetToken }: UpdatePasswordProps) => { + const mutate = async ({ password, resetToken }: UpdatePasswordVariables) => { const updatePasswordInput = { password, resetToken }; try { @@ -33,7 +30,7 @@ const useUpdatePassword = () => { } }; - return [mutate, mutationResult] as const; + return [mutate, mutationResult]; }; export default useUpdatePassword; From e1d8d4c101d86c31b2977c1405bc0f2b9b635bba Mon Sep 17 00:00:00 2001 From: Regina-Gimazova Date: Wed, 8 Jun 2022 14:07:11 +0300 Subject: [PATCH 4/5] fix some comments --- components/pages/newPassword/NewPasswordPage.jsx | 2 +- .../components/NewPasswordForm/NewPasswordForm.tsx | 9 ++++----- .../pages/signUp/components/SignUpForm/SignUpForm.tsx | 5 ++--- config/passwordRegExp.ts | 1 + 4 files changed, 8 insertions(+), 9 deletions(-) create mode 100644 config/passwordRegExp.ts diff --git a/components/pages/newPassword/NewPasswordPage.jsx b/components/pages/newPassword/NewPasswordPage.jsx index b4c20344..a5e2926e 100644 --- a/components/pages/newPassword/NewPasswordPage.jsx +++ b/components/pages/newPassword/NewPasswordPage.jsx @@ -17,7 +17,7 @@ import { PageContentWrapper } from './styled'; const NewPasswordPage = ({ query }) => { return ( - + diff --git a/components/pages/newPassword/components/NewPasswordForm/NewPasswordForm.tsx b/components/pages/newPassword/components/NewPasswordForm/NewPasswordForm.tsx index a8ebca38..ee99b93e 100644 --- a/components/pages/newPassword/components/NewPasswordForm/NewPasswordForm.tsx +++ b/components/pages/newPassword/components/NewPasswordForm/NewPasswordForm.tsx @@ -9,11 +9,10 @@ import Button from 'components/shared/atoms/Button'; import FormFieldInput from 'components/shared/atoms/FormField'; import Loader from 'components/shared/atoms/Loader'; import { FormFieldType } from 'types/formsType'; +import passwordRegExp from 'config/passwordRegExp'; import { FieldWrapper, FormContentWrapper, SubmitButtonWrapper } from './styled'; -const passwordRegularExp = /^(?=.*[0-9])(?=.*[A-Z])(?=.*[a-z])([0-9A-Za-z#$@&!?.*^{}<>;,)(~'"=_%+-]+)$/; - const initialValues = { password: '', passwordConfirmation: '', @@ -24,18 +23,18 @@ const SignInValidationSchema = Yup.object().shape({ .required('This field is required') .trim() .min(6, 'The minimum password length is 6 characters') - .matches(passwordRegularExp, 'Password must contain upper and lower case characters and numbers'), + .matches(passwordRegExp, 'Password must contain upper and lower case characters and numbers'), passwordConfirmation: Yup.string() .oneOf([Yup.ref('password')], 'Password does not match') .required('This field is required'), }); -type ValuesFromFormik = { +type FormValues = { password: string; passwordConfirmation: string; }; -const SignInFormContent = ({ isSubmitting }: FormikProps) => ( +const SignInFormContent = ({ isSubmitting }: FormikProps) => (
diff --git a/components/pages/signUp/components/SignUpForm/SignUpForm.tsx b/components/pages/signUp/components/SignUpForm/SignUpForm.tsx index da3ad380..e660ad7b 100644 --- a/components/pages/signUp/components/SignUpForm/SignUpForm.tsx +++ b/components/pages/signUp/components/SignUpForm/SignUpForm.tsx @@ -8,11 +8,10 @@ import Button from 'components/shared/atoms/Button'; import FormFieldInput from 'components/shared/atoms/FormField'; import Loader from 'components/shared/atoms/Loader'; import { FormFieldType } from 'types/formsType'; +import passwordRegExp from 'config/passwordRegExp'; import { FieldWrapper, FormContentWrapper, SubmitButtonWrapper } from './styled'; -const passwordRegularExp = /^(?=.*[0-9])(?=.*[A-Z])(?=.*[a-z])([0-9A-Za-z#$@&!?.*^{}<>;,)(~'"=_%+-]+)$/; - const initialValues = { firstName: '', lastName: '', @@ -28,7 +27,7 @@ const SignUpValidationSchema = Yup.object().shape({ .required('This field is required') .trim() .min(6, 'The minimum password length is 6 characters') - .matches(passwordRegularExp, 'Password must contain upper and lower case characters and numbers'), + .matches(passwordRegExp, 'Password must contain upper and lower case characters and numbers'), }); type ValuesFromFormik = Parameters[0]>[0]; diff --git a/config/passwordRegExp.ts b/config/passwordRegExp.ts new file mode 100644 index 00000000..e67be637 --- /dev/null +++ b/config/passwordRegExp.ts @@ -0,0 +1 @@ +export default /^(?=.*[0-9])(?=.*[A-Z])(?=.*[a-z])([0-9A-Za-z#$@&!?.*^{}<>;,)(~'"=_%+-]+)$/; From 247c1dc09abfc510cf05b368c1662ae6eb61c045 Mon Sep 17 00:00:00 2001 From: Regina-Gimazova Date: Wed, 8 Jun 2022 14:27:33 +0300 Subject: [PATCH 5/5] fix some comments --- api/cache/read/useReadHasCurrentUserCache.ts | 3 ++- .../components/NewPasswordForm/NewPasswordForm.tsx | 10 +++++----- .../fragments/{currentUser => }/hasCurrentUser.graphql | 0 graphql/mutations/updatePassword.graphql | 1 + 4 files changed, 8 insertions(+), 6 deletions(-) rename graphql/fragments/{currentUser => }/hasCurrentUser.graphql (100%) diff --git a/api/cache/read/useReadHasCurrentUserCache.ts b/api/cache/read/useReadHasCurrentUserCache.ts index 830c716e..2fe12999 100644 --- a/api/cache/read/useReadHasCurrentUserCache.ts +++ b/api/cache/read/useReadHasCurrentUserCache.ts @@ -1,6 +1,7 @@ import type { ApolloClient, InMemoryCache } from '@apollo/client'; -import HasCurrentUserFragment from 'graphql/fragments/currentUser/hasCurrentUser.graphql'; +// TODO: Maybe rename query? +import HasCurrentUserFragment from 'graphql/fragments/hasCurrentUser.graphql'; import type { Me } from 'api/types/user/user'; import { readCacheFragmentApi } from 'api/hooks/useReadCacheFragmentHook'; diff --git a/components/pages/newPassword/components/NewPasswordForm/NewPasswordForm.tsx b/components/pages/newPassword/components/NewPasswordForm/NewPasswordForm.tsx index ee99b93e..ca8a1746 100644 --- a/components/pages/newPassword/components/NewPasswordForm/NewPasswordForm.tsx +++ b/components/pages/newPassword/components/NewPasswordForm/NewPasswordForm.tsx @@ -13,11 +13,6 @@ import passwordRegExp from 'config/passwordRegExp'; import { FieldWrapper, FormContentWrapper, SubmitButtonWrapper } from './styled'; -const initialValues = { - password: '', - passwordConfirmation: '', -}; - const SignInValidationSchema = Yup.object().shape({ password: Yup.string() .required('This field is required') @@ -34,6 +29,11 @@ type FormValues = { passwordConfirmation: string; }; +const initialValues: FormValues = { + password: '', + passwordConfirmation: '', +}; + const SignInFormContent = ({ isSubmitting }: FormikProps) => ( diff --git a/graphql/fragments/currentUser/hasCurrentUser.graphql b/graphql/fragments/hasCurrentUser.graphql similarity index 100% rename from graphql/fragments/currentUser/hasCurrentUser.graphql rename to graphql/fragments/hasCurrentUser.graphql diff --git a/graphql/mutations/updatePassword.graphql b/graphql/mutations/updatePassword.graphql index 16b681cc..4a5cd28b 100644 --- a/graphql/mutations/updatePassword.graphql +++ b/graphql/mutations/updatePassword.graphql @@ -1,5 +1,6 @@ mutation UpdatePassword($input: UpdatePasswordInput!) { updatePassword(input: $input) { accessToken + refreshToken } }