Skip to content

Commit

Permalink
feat(user): add reset password logic
Browse files Browse the repository at this point in the history
  • Loading branch information
RCVZ committed Jul 30, 2021
1 parent 86c75ab commit c0b9681
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@ exports[`<RegistrationForm> renders and matches snapshot 1`] = `
for="text-field_1235_email"
>
registration.email
<span>
optional
</span>
</label>
<div
class="container"
Expand All @@ -31,6 +28,7 @@ exports[`<RegistrationForm> renders and matches snapshot 1`] = `
id="text-field_1235_email"
name="email"
placeholder="registration.email"
required=""
type="email"
value=""
/>
Expand All @@ -44,9 +42,6 @@ exports[`<RegistrationForm> renders and matches snapshot 1`] = `
for="text-field_1235_password"
>
registration.password
<span>
optional
</span>
</label>
<div
class="container"
Expand All @@ -56,6 +51,7 @@ exports[`<RegistrationForm> renders and matches snapshot 1`] = `
id="text-field_1235_password"
name="password"
placeholder="registration.password"
required=""
type="password"
value=""
/>
Expand Down
12 changes: 9 additions & 3 deletions src/containers/AccountModal/AccountModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import ChooseOffer from './forms/ChooseOffer';
import Checkout from './forms/Checkout';
import ResetPassword from './forms/ResetPassword';

const PUBLIC_VIEWS = ['login', 'create-account', 'forgot-password', 'reset-password'];
const PUBLIC_VIEWS = ['login', 'create-account', 'forgot-password', 'reset-password', 'send-confirmation', 'edit-password'];

const AccountModal = () => {
const history = useHistory();
Expand Down Expand Up @@ -60,8 +60,6 @@ const AccountModal = () => {
return <Login />;
case 'create-account':
return <Registration />;
case 'reset-password':
return <ResetPassword />;
case 'personal-details':
return <PersonalDetails />;
case 'choose-offer':
Expand All @@ -74,6 +72,14 @@ const AccountModal = () => {
return <PaymentFailed type="cancelled" onCloseButtonClick={closeHandler} />;
case 'welcome':
return <Welcome onCloseButtonClick={closeHandler} onCountdownCompleted={closeHandler} />;
case 'reset-password':
return <ResetPassword type="reset" />;
case 'forgot-password':
return <ResetPassword type="forgot" />;
case 'send-confirmation':
return <ResetPassword type="confirmation" />;
case 'edit-password':
return <ResetPassword type="edit" />;
}
};

Expand Down
108 changes: 101 additions & 7 deletions src/containers/AccountModal/forms/ResetPassword.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,39 @@
import React from 'react';
import { useHistory } from 'react-router-dom';
import { object, string } from 'yup';
import { useTranslation } from 'react-i18next';

import { resetPassword } from '../../../stores/AccountStore';
import { removeQueryParam } from '../../../utils/history';
import { resetPassword, AccountStore, changePassword } from '../../../stores/AccountStore';
import { removeQueryParam, addQueryParam } from '../../../utils/history';
import ResetPasswordForm from '../../../components/ResetPasswordForm/ResetPasswordForm';
import useForm, { UseFormOnSubmitHandler } from '../../../hooks/useForm';
import ForgotPasswordForm from '../../../components/ForgotPasswordForm/ForgotPasswordForm';
import type { EmailField, PasswordField } from '../../../../types/account';
import EditPasswordForm from '../../../components/EditPasswordForm/EditPasswordForm';
import useQueryParam from '../../../hooks/useQueryParam';

const ResetPassword: React.FC = () => {
type Prop = {
type: 'confirmation' | 'forgot' | 'reset' | 'edit';
};

const ResetPassword: React.FC<Prop> = ({ type }: Prop) => {
const { t } = useTranslation('account');
const history = useHistory();
const user = AccountStore.useState((state) => state.user);
const resetPasswordTokenParam = useQueryParam('resetPasswordToken');
const emailParam = useQueryParam('email');

const onCancelClickHandler = () => {
const cancelClickHandler = () => {
history.push(removeQueryParam(history, 'u'));
};

const onResetClickHandler = async () => {
const resetPasswordClickHandler = async () => {
const resetUrl = `${window.location.origin}/u/my-account?u=edit-password`;

try {
await resetPassword(resetUrl);
if (!user?.email) throw new Error('invalid param email');

await resetPassword(user.email, resetUrl);

history.push('/u/logout');
} catch (error: unknown) {
Expand All @@ -28,7 +45,84 @@ const ResetPassword: React.FC = () => {
}
};

return <ResetPasswordForm onCancel={onCancelClickHandler} onReset={onResetClickHandler} />;
const emailSubmitHandler: UseFormOnSubmitHandler<EmailField> = async (formData, { setErrors, setSubmitting }) => {
const resetUrl = `${window.location.origin}/u/my-account?u=edit-password`;

try {
await resetPassword(formData.email, resetUrl);
history.push(addQueryParam(history, 'u', 'send-confirmation'));
} catch (error: unknown) {
if (error instanceof Error) {
if (error.message.toLowerCase().includes('invalid param email')) {
setErrors({ email: t('reset.wrong_email') });
}
}
}
setSubmitting(false);
};

const passwordSubmitHandler: UseFormOnSubmitHandler<PasswordField> = async (formData, { setErrors, setSubmitting, setValue }) => {
try {
if (!resetPasswordTokenParam || !emailParam) throw new Error('invalid reset link');

await changePassword(emailParam, formData.password, resetPasswordTokenParam);
history.push(addQueryParam(history, 'u', 'login'));
} catch (error: unknown) {
if (error instanceof Error) {
if (error.message.includes('invalid param password')) {
setErrors({ password: t('reset.invalid_password') });
} else if (error.message.includes('resetPasswordToken is not valid')) {
setErrors({ password: t('reset.invalid_token') });
} else if (error.message.includes('invalid reset link')) {
setErrors({ password: t('reset.invalid_link') });
}

setValue('password', '');
}
}
setSubmitting(false);
};

const emailForm = useForm(
{ email: '' },
emailSubmitHandler,
object().shape({
email: string().email(t('login.field_is_not_valid_email')).required(t('login.field_required')),
}),
);

const passwordForm = useForm(
{ password: '' },
passwordSubmitHandler,
object().shape({
password: string().required(t('login.field_required')),
}),
);

return (
<React.Fragment>
{type === 'reset' && <ResetPasswordForm onCancel={cancelClickHandler} onReset={resetPasswordClickHandler} />}
{(type === 'forgot' || type === 'confirmation') && (
<ForgotPasswordForm
value={emailForm.values}
submitting={emailForm.submitting}
onChange={emailForm.handleChange}
errors={emailForm.errors}
onSubmit={emailForm.handleSubmit}
type={type}
/>
)}
{type === 'edit' && (
<EditPasswordForm
value={passwordForm.values}
submitting={passwordForm.submitting}
onChange={passwordForm.handleChange}
errors={passwordForm.errors}
onSubmit={passwordForm.handleSubmit}
/>
)}
</React.Fragment>
);
};

export default ResetPassword;
21 changes: 20 additions & 1 deletion src/i18n/locales/en_US/account.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,28 @@
"email_updates": "Yes, I want to receive {{siteName}} updates by email."
},
"reset": {
"back_login": "Back to login",
"confirm": "Confirm",
"email": "Email",
"email_me": "Email me",
"forgot_password": "Forgot password",
"forgot_text": "We will send you an email with instructions on how to reset your password.",
"hide_password": "Hide password",
"invalid_link": "Invalid link",
"invalid_password": "Invalid password",
"invalid_token": "Invalid link",
"link_sent": "Password link sent",
"link_sent_text": "Please check your inbox at {{email}}",
"no": "No, thanks",
"not_sure": "Not sure that was the right email address?",
"password": "Password",
"password_reset": "Password reset",
"reset_password": "Edit Password",
"text": "If you want to edit your password, click 'YES, Reset' to receive password reset instruction on your mail",
"yes": "Yes, reset"
"try_again": "Try again",
"view_password": "View password",
"wrong_email": "Wrong email",
"yes": "Yes, reset",
"password_reset_text": ""
}
}
21 changes: 20 additions & 1 deletion src/i18n/locales/nl_NL/account.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,28 @@
"email_updates": ""
},
"reset": {
"back_login": "",
"confirm": "",
"email": "",
"email_me": "",
"forgot_password": "",
"forgot_text": "",
"hide_password": "",
"invalid_link": "",
"invalid_password": "",
"invalid_token": "",
"link_sent": "",
"link_sent_text": "",
"no": "",
"not_sure": "",
"password": "",
"password_reset": "",
"reset_password": "",
"text": "",
"yes": ""
"try_again": "",
"view_password": "",
"wrong_email": "",
"yes": "",
"password_reset_text": ""
}
}
28 changes: 24 additions & 4 deletions src/stores/AccountStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,18 +205,16 @@ export const updateCaptureAnswers = async (capture: Capture) => {
return response.responseData;
};

export const resetPassword = async (resetUrl: string) => {
export const resetPassword = async (email: string, resetUrl: string) => {
const {
config: { cleengId, cleengSandbox },
} = ConfigStore.getRawState();
const { user } = AccountStore.getRawState();

if (!cleengId) throw new Error('cleengId is not configured');
if (!user?.email) throw new Error('invalid param email');

const response = await accountService.resetPassword(
{
customerEmail: user.email,
customerEmail: email,
publisherId: cleengId,
resetUrl,
},
Expand All @@ -227,3 +225,25 @@ export const resetPassword = async (resetUrl: string) => {

return response.responseData;
};

export const changePassword = async (customerEmail: string, newPassword: string, resetPasswordToken: string) => {
const {
config: { cleengId, cleengSandbox },
} = ConfigStore.getRawState();

if (!cleengId) throw new Error('cleengId is not configured');

const response = await accountService.changePassword(
{
publisherId: cleengId,
customerEmail,
newPassword,
resetPasswordToken,
},
cleengSandbox,
);

if (response.errors.length > 0) throw new Error(response.errors[0]);

return response.responseData;
};
10 changes: 9 additions & 1 deletion types/account.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ export type RegistrationFormData = {
password: string;
};

export type EmailField = {
email: string;
};

export type PasswordField = {
password: string;
};

export type OfferPeriodicity = 'monthly' | 'yearly';

export type ChooseOfferFormData = {
Expand Down Expand Up @@ -223,7 +231,7 @@ export type Capture = {
companyName?: string;
phoneNumber?: string;
customAnswers?: CaptureCustomAnswer[];
}
};

export type UpdateCaptureAnswersPayload = {
customerId: string;
Expand Down

0 comments on commit c0b9681

Please sign in to comment.