Skip to content

Commit

Permalink
feat(user): add ability to add password to a social account
Browse files Browse the repository at this point in the history
feat(user): add resend password reset email button
  • Loading branch information
naumovski-filip committed Nov 15, 2023
1 parent 0e92a58 commit b3aa739
Show file tree
Hide file tree
Showing 10 changed files with 227 additions and 21 deletions.
10 changes: 10 additions & 0 deletions public/locales/en/user.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
{
"account": {
"about_you": "Profile info",
"add_password": "Add password",
"add_password_error": "Failed to proceed to the Add Password screen. Please try again.",
"add_password_modal_text": "We sent instructions to {{email}} on how to add a password. If you don’t receive the email within a few minutes, check your spam folder or",
"add_profile": "Add profile",
"cancel": "Cancel",
"confirm_password": "Confirm password",
Expand All @@ -20,6 +23,10 @@
},
"title": "Delete account"
},
"delete_account_password_warning": {
"text": "To delete your account when using social media account(s) for sign-in, you will first need to add an account password and then initiate the deletion process again.",
"title": "Warning"
},
"edit_account": "Edit account",
"edit_information": "Edit information",
"edit_password": "Edit password",
Expand All @@ -44,6 +51,9 @@
"manage_profiles": "Manage profiles",
"other_registration_details": "Other registration details",
"password": "Password",
"proceed_to_adding_a_password": "Proceed to adding a password",
"resend_mail": "resend the email.",
"resend_mail_error": "An error occurred while resending the email. Please try again.",
"save": "Save",
"security": "Password",
"terms_and_tracking": "Legal & Marketing",
Expand Down
10 changes: 10 additions & 0 deletions public/locales/es/user.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
{
"account": {
"about_you": "Información de perfil",
"add_password": "Agregar contraseña",
"add_password_error": "No se pudo pasar a la pantalla Agregar contraseña. Inténtalo de nuevo.",
"add_password_modal_text": "Enviamos instrucciones a {{email}} sobre cómo agregar una contraseña. Si no recibes el correo electrónico en unos minutos, revisa tu carpeta de spam o",
"add_profile": "Añadir perfil",
"cancel": "Cancelar",
"confirm_password": "Confirmar contraseña",
Expand All @@ -20,6 +23,10 @@
},
"title": "Borrar cuenta"
},
"delete_account_password_warning": {
"text": "Para eliminar su cuenta cuando utiliza cuentas de redes sociales para iniciar sesión, primero deberá agregar una contraseña de cuenta y luego iniciar el proceso de eliminación nuevamente.",
"title": "Advertencia"
},
"edit_account": "Editar cuenta",
"edit_information": "Editar información",
"edit_password": "Editar contraseña",
Expand All @@ -44,6 +51,9 @@
"manage_profiles": "Administrar perfiles",
"other_registration_details": "Otros detalles de registro",
"password": "Contraseña",
"proceed_to_adding_a_password": "Proceda a agregar una contraseña",
"resend_mail": "reenvíe el correo electrónico.",
"resend_mail_error": "Se produjo un error al reenviar el correo electrónico. Inténtalo de nuevo.",
"save": "Guardar",
"security": "Contraseña",
"terms_and_tracking": "Jurídico y marketing",
Expand Down
26 changes: 22 additions & 4 deletions src/components/Account/Account.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import useToggle from '#src/hooks/useToggle';
import { formatConsentsFromValues, formatConsents, formatConsentValues, formatConsentsToRegisterFields } from '#src/utils/collection';
import { addQueryParam } from '#src/utils/location';
import { useAccountStore } from '#src/stores/AccountStore';
import { exportAccountData, updateConsents, updateUser } from '#src/stores/AccountController';
import { exportAccountData, resetPassword, updateConsents, updateUser } from '#src/stores/AccountController';

type Props = {
panelClassName?: string;
Expand Down Expand Up @@ -67,6 +67,11 @@ const Account = ({ panelClassName, panelHeaderClassName, canUpdateEmail = true }
shallow,
);

// users authenticated with social (register_source: facebook, google, twitter) do not have password by default
const registerSource = customer?.metadata?.register_source;
const isSocialLogin = (registerSource && registerSource !== 'inplayer') || false;
const shouldAddPassword = (isSocialLogin && !customer?.metadata?.has_password) || false;

const [termsConsents, nonTermsConsents] = useMemo(() => {
const terms: Consent[] = [];
const nonTerms: Consent[] = [];
Expand Down Expand Up @@ -173,7 +178,14 @@ const Account = ({ panelClassName, panelHeaderClassName, canUpdateEmail = true }
};
}

const editPasswordClickHandler = () => {
const editPasswordClickHandler = async () => {
if (!customer) {
return;
}
if (isSocialLogin && shouldAddPassword) {
await resetPassword(customer.email, '');
return navigate(addQueryParam(location, 'u', 'add-password'));
}
const modal = canChangePasswordWithOldPassword ? 'edit-password' : 'reset-password';
navigate(addQueryParam(location, 'u', modal));
};
Expand Down Expand Up @@ -269,7 +281,13 @@ const Account = ({ panelClassName, panelHeaderClassName, canUpdateEmail = true }
}),
formSection({
label: t('account.security'),
editButton: <Button label={t('account.edit_password')} type="button" onClick={() => (customer ? editPasswordClickHandler() : null)} />,
editButton: (
<Button
label={shouldAddPassword ? t('account.add_password') : t('account.edit_password')}
type="button"
onClick={() => (customer ? editPasswordClickHandler() : null)}
/>
),
}),
formSection({
label: t('account.terms_and_tracking'),
Expand Down Expand Up @@ -347,7 +365,7 @@ const Account = ({ panelClassName, panelHeaderClassName, canUpdateEmail = true }
type="button"
variant="danger"
onClick={() => {
navigate(addQueryParam(location, 'u', 'delete-account'));
navigate(addQueryParam(location, 'u', shouldAddPassword ? 'warning-account-deletion' : 'delete-account'));
}}
/>
</div>
Expand Down
1 change: 0 additions & 1 deletion src/components/DeleteAccountModal/DeleteAccountModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ const DeleteAccountModal = () => {
if (!location.search.includes('delete-account-confirmation') && enteredPassword) {
// handle back button
setEnteredPassword('');
deleteAccount.reset();
resetForm();
}
if (location.search.includes('delete-account-confirmation') && !enteredPassword) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
@use 'src/styles/variables';

.formFeedback {
margin-bottom: -24px;
}

.formContainer {
display: flex;
flex-direction: column;
gap: 15px;
align-items: center;
padding: 27px;
}

.passwordButtonsContainer {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
width: 100%;
}

.buttonsContainer {
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
width: 100%;
gap: 16px;
}

.heading {
width: 50%;
margin-bottom: 5px;
color: variables.$gray-white;
font-weight: 400;
font-size: 30px;
line-height: 28px;
text-align: center;
}

.paragraph {
width: 100%;
margin: 0;
padding: 0;
color: variables.$gray-white;
font-size: 16px;
line-height: 20px;
text-align: left;
}

.button {
margin: auto;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { useTranslation } from 'react-i18next';
import { useNavigate, useLocation } from 'react-router';
import { useCallback, useState } from 'react';

import Button from '../Button/Button';

import styles from './DeleteAccountPasswordWarning.module.scss';

import { addQueryParam, removeQueryParam } from '#src/utils/location';
import { useAccountStore } from '#src/stores/AccountStore';
import { resetPassword } from '#src/stores/AccountController';
import FormFeedback from '#components/FormFeedback/FormFeedback';

const DeleteAccountPasswordWarning = () => {
const { t } = useTranslation('user');
const email = useAccountStore((state) => state.user?.email);
const [errorMessage, setErrorMessage] = useState<string>();
const navigate = useNavigate();
const location = useLocation();

const handleCancel = useCallback(() => {
navigate(removeQueryParam(location, 'u'), { replace: true });
}, [location, navigate]);
const proceedToAddPasswordClickHandler = async () => {
try {
if (email) {
await resetPassword(email, '');
navigate(addQueryParam(location, 'u', 'add-password'));
}
} catch (error: unknown) {
setErrorMessage(t('account.add_password_error'));
}
};

return (
<div className={styles.formContainer}>
{errorMessage && (
<div className={styles.formFeedback}>
<FormFeedback variant="error">{errorMessage}</FormFeedback>
</div>
)}
<h2 className={styles.heading}>{t('account.delete_account_password_warning.title')}</h2>
<p className={styles.paragraph}>{t('account.delete_account_password_warning.text')}</p>
<div className={styles.passwordButtonsContainer}>
<Button
type="submit"
onClick={proceedToAddPasswordClickHandler}
className={styles.button}
color="primary"
fullWidth
label={t('account.proceed_to_adding_a_password')}
/>
<Button type="button" onClick={handleCancel} className={styles.button} variant="text" fullWidth label={t('account.cancel')} />
</div>
</div>
);
};

export default DeleteAccountPasswordWarning;
17 changes: 17 additions & 0 deletions src/components/EditPasswordForm/EditPasswordForm.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,20 @@
.textField {
margin-bottom: 24px;
}

.paragraph {
width: 100%;
margin: 0;
padding-bottom: 20px;
color: variables.$gray-white;
font-size: 17px;
line-height: 21px;
text-align: left;
}

.resendLink {
padding-left: 4px;
color: rgb(129, 175, 254);
cursor: pointer;

}
43 changes: 32 additions & 11 deletions src/components/EditPasswordForm/EditPasswordForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import TextField from '../TextField/TextField';

import styles from './EditPasswordForm.module.scss';

import type { FormErrors } from '#types/form';
import type { EditPasswordFormData } from '#types/account';
import FormFeedback from '#components/FormFeedback/FormFeedback';
import Button from '#components/Button/Button';
import FormFeedback from '#components/FormFeedback/FormFeedback';
import LoadingOverlay from '#components/LoadingOverlay/LoadingOverlay';
import { testId } from '#src/utils/common';
import type { EditPasswordFormData } from '#types/account';
import type { FormErrors } from '#types/form';

type Props = {
onSubmit: React.FormEventHandler<HTMLFormElement>;
Expand All @@ -24,16 +24,36 @@ type Props = {
submitting: boolean;
showOldPasswordField?: boolean;
showResetTokenField?: boolean;
email?: string;
onResendEmailClick?: () => void;
};

const EditPasswordForm: React.FC<Props> = ({ onSubmit, onChange, onBlur, showOldPasswordField, showResetTokenField, value, errors, submitting }: Props) => {
const { t } = useTranslation('account');
const EditPasswordForm: React.FC<Props> = ({
onSubmit,
onChange,
onBlur,
showOldPasswordField,
showResetTokenField,
value,
errors,
submitting,
email,
onResendEmailClick,
}: Props) => {
const { t } = useTranslation(['account', 'user']);
return (
<form onSubmit={onSubmit} data-testid={testId('forgot-password-form')} noValidate className={styles.forgotPasswordForm}>
<h2 className={styles.title}>{t('reset.password_reset')}</h2>
{errors.form ? <FormFeedback variant="error">{errors.form}</FormFeedback> : null}

{showOldPasswordField ? (
{errors.form && <FormFeedback variant="error">{errors.form}</FormFeedback>}
<h2 className={styles.title}>{showOldPasswordField && showResetTokenField ? t('user:account.add_password') : t('reset.password_reset')}</h2>
{showOldPasswordField && showResetTokenField && (
<p className={styles.paragraph}>
{t('user:account.add_password_modal_text', { email: email })}
<a className={styles.resendLink} onClick={onResendEmailClick}>
{t('user:account.resend_mail')}
</a>
</p>
)}
{showOldPasswordField && !showResetTokenField && (
<PasswordField
value={value.oldPassword}
onChange={onChange}
Expand All @@ -46,7 +66,8 @@ const EditPasswordForm: React.FC<Props> = ({ onSubmit, onChange, onBlur, showOld
showHelperText={false}
required
/>
) : showResetTokenField ? (
)}
{showResetTokenField && (
<TextField
className={styles.textField}
value={value.resetPasswordToken || ''}
Expand All @@ -58,7 +79,7 @@ const EditPasswordForm: React.FC<Props> = ({ onSubmit, onChange, onBlur, showOld
type="text"
required
/>
) : null}
)}

<PasswordField
value={value.password}
Expand Down
7 changes: 6 additions & 1 deletion src/containers/AccountModal/AccountModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import WaitingForPayment from '#components/WaitingForPayment/WaitingForPayment';
import UpdatePaymentMethod from '#src/containers/UpdatePaymentMethod/UpdatePaymentMethod';
import useEventCallback from '#src/hooks/useEventCallback';
import UpgradeSubscription from '#components/UpgradeSubscription/UpgradeSubscription';
import DeleteAccountPasswordWarning from '#components/DeleteAccountPasswordWarning/DeleteAccountPasswordWarning';

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

Expand Down Expand Up @@ -104,9 +105,13 @@ const AccountModal = () => {
return <ResetPassword type="reset" />;
case 'forgot-password':
return <ResetPassword type="forgot" />;
case 'add-password':
return <EditPassword type="add" />;
case 'delete-account':
case 'delete-account-confirmation':
return <DeleteAccountModal />;
case 'warning-account-deletion':
return <DeleteAccountPasswordWarning />;
case 'send-confirmation':
return <ResetPassword type="confirmation" />;
case 'edit-password':
Expand All @@ -125,7 +130,7 @@ const AccountModal = () => {
}
};

const shouldShowBanner = !['delete-account', 'delete-account-confirmation', 'edit-card'].includes(view ?? '');
const shouldShowBanner = !['delete-account', 'delete-account-confirmation', 'edit-card', 'warning-account-deletion'].includes(view ?? '');
const dialogSize = ['delete-account-confirmation'].includes(view ?? '') ? 'large' : 'small';

return (
Expand Down
Loading

0 comments on commit b3aa739

Please sign in to comment.