Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion api/cache/read/useReadHasCurrentUserCache.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down
26 changes: 26 additions & 0 deletions api/mutations/update/useUpdatePasswordMutation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { MutationHookOptions, MutationTuple, MutationResult } from '@apollo/client';

import UpdatePasswordMutation from 'graphql/mutations/updatePassword.graphql';

import type { UpdatePasswordVariables, UpdatePasswordData } from '../../types/user/updatePasswordApiType';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be the absolute path

import useMutation from '../../hooks/useMutationHook';

type UpdatePasswordResponseData = {
updatePassword: UpdatePasswordData;
};

type UpdatePasswordRequestVariables = {
input: UpdatePasswordVariables;
};

type UpdatePasswordMutationOptions = MutationHookOptions<UpdatePasswordResponseData, UpdatePasswordRequestVariables>;

type UpdatePasswordMutationTuple = MutationTuple<UpdatePasswordResponseData, UpdatePasswordRequestVariables>;

export type UpdatePasswordMutationResult = MutationResult<UpdatePasswordResponseData>;

const useUpdatePasswordMutation = (options: UpdatePasswordMutationOptions): UpdatePasswordMutationTuple => {
return useMutation<UpdatePasswordResponseData, UpdatePasswordRequestVariables>(UpdatePasswordMutation, options);
};

export default useUpdatePasswordMutation;
8 changes: 8 additions & 0 deletions api/types/user/updatePasswordApiType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export type UpdatePasswordData = {
accessToken: string;
};

export type UpdatePasswordVariables = {
password: string;
resetToken: string | string[] | undefined;
};
41 changes: 41 additions & 0 deletions components/pages/newPassword/NewPasswordPage.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<NotifierProvider>
<DefaultTemplate testId="new-password-page">
<PageContentWrapper>
<NewPasswordForm query={query} />
</PageContentWrapper>
<Notifier />
</DefaultTemplate>
</NotifierProvider>
);
};

NewPasswordPage.getInitialProps = ({ res, accessTokenManager }) => {
if (accessTokenManager.accessToken) {
if (res) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know why, but in other files we use the following conditions if (!!req && !!res) {

Copy link
Contributor

@Regina-Gimazova Regina-Gimazova Jun 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now for all pages we check only res, so I think we can leave it

res.redirect(302, HOME);
} else {
Router.push(HOME);
}
}
return {};
};

export default withApolloClient(WithAuth(NewPasswordPage));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't you use WithAuthSecurity HOC?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can leave as is, cause almost all pages do not use this HOC. I'll take a look at this in next prs

Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
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 passwordRegExp from 'config/passwordRegExp';

import { FieldWrapper, FormContentWrapper, SubmitButtonWrapper } from './styled';

const SignInValidationSchema = Yup.object().shape({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SignIn

password: Yup.string()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better create file with yup validation

.required('This field is required')
.trim()
.min(6, 'The minimum password length is 6 characters')
.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 FormValues = {
password: string;
passwordConfirmation: string;
};

const initialValues: FormValues = {
password: '',
passwordConfirmation: '',
};

const SignInFormContent = ({ isSubmitting }: FormikProps<FormValues>) => (
<FormContentWrapper>
<Form>
<FieldWrapper>
<FormFieldInput name="password" type={FormFieldType.password} label="New Password" />
</FieldWrapper>
<FieldWrapper>
<FormFieldInput name="passwordConfirmation" type={FormFieldType.password} label="New Password Confirmation" />
</FieldWrapper>
<SubmitButtonWrapper>
<Button type={FormFieldType.submit} testID="submit-button" disabled={isSubmitting}>
Submit
</Button>
</SubmitButtonWrapper>
</Form>
</FormContentWrapper>
);

const NewPasswordForm = () => {
const { query } = useRouter();
const { reset_token: resetToken } = query;
const [updatePassword, updatePasswordState] = useUpdatePassword();

const onSubmit = async ({ password }: { password: string }) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

password: Omit<TData, 'resetToken'>

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TData = UpdatePasswordVariables

await updatePassword({ password, resetToken });
};

return (
<>
<Formik
initialValues={initialValues}
onSubmit={onSubmit}
component={SignInFormContent}
validationSchema={SignInValidationSchema}
/>
{updatePasswordState.loading && <Loader testId="form-loader">Loading...</Loader>}
</>
);
};

export default NewPasswordForm;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './NewPasswordForm';
13 changes: 13 additions & 0 deletions components/pages/newPassword/components/NewPasswordForm/styled.ts
Original file line number Diff line number Diff line change
@@ -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;
`;
1 change: 1 addition & 0 deletions components/pages/newPassword/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './NewPasswordPage';
6 changes: 6 additions & 0 deletions components/pages/newPassword/styled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import styled from 'styled-components';

export const PageContentWrapper = styled.div`
display: flex;
justify-content: center;
`;
5 changes: 2 additions & 3 deletions components/pages/signUp/components/SignUpForm/SignUpForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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: '',
Expand All @@ -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<ReturnType<typeof useSignUp>[0]>[0];
Expand Down
1 change: 1 addition & 0 deletions config/passwordRegExp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default /^(?=.*[0-9])(?=.*[A-Z])(?=.*[a-z])([0-9A-Za-z#$@&!?.*^{}<>;,)(~'"=_%+-]+)$/;
1 change: 1 addition & 0 deletions config/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
6 changes: 6 additions & 0 deletions graphql/mutations/updatePassword.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
mutation UpdatePassword($input: UpdatePasswordInput!) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oyyy I didn't know what type for $input existed
Maybe better to rename all input type for mutation

Copy link
Contributor

@Regina-Gimazova Regina-Gimazova Jun 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry, but I don't understand your point. I think we can leave as is for now

updatePassword(input: $input) {
accessToken
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

refreshToken

refreshToken
}
}
36 changes: 36 additions & 0 deletions lib/apollo/hooks/actions/useUpdatePassword.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
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';

const useUpdatePassword = (): [(variables: UpdatePasswordVariables) => Promise<void>, UpdatePasswordMutationResult] => {
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 }: UpdatePasswordVariables) => {
const updatePasswordInput = { password, resetToken };

try {
await mutation({ variables: { input: updatePasswordInput } });
} catch (error) {
if (setError) setError(error);
}
};

return [mutate, mutationResult];
};

export default useUpdatePassword;
1 change: 1 addition & 0 deletions pages/new-password.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from 'components/pages/newPassword';