RocketChat/Rocket.Chat

View on GitHub
packages/web-ui-registration/src/ResetPassword/ResetPasswordPage.tsx

Summary

Maintainability
C
1 day
Test Coverage
import { Button, FieldGroup, Field, FieldLabel, ButtonGroup, PasswordInput, FieldRow, FieldError } from '@rocket.chat/fuselage';
import { useUniqueId } from '@rocket.chat/fuselage-hooks';
import { Form } from '@rocket.chat/layout';
import { PasswordVerifier, useValidatePassword } from '@rocket.chat/ui-client';
import type { TranslationKey } from '@rocket.chat/ui-contexts';
import { useSetting, useRouter, useRouteParameter, useUser, useMethod, useTranslation, useLoginWithToken } from '@rocket.chat/ui-contexts';
import type { ReactElement } from 'react';
import { useEffect, useRef } from 'react';
import { useForm } from 'react-hook-form';

import HorizontalTemplate from '../template/HorizontalTemplate';

const getChangePasswordReason = ({
    requirePasswordChange,
    requirePasswordChangeReason = requirePasswordChange ? 'You_need_to_change_your_password' : 'Please_enter_your_new_password_below',
}: { requirePasswordChange?: boolean; requirePasswordChangeReason?: TranslationKey } = {}): TranslationKey => requirePasswordChangeReason;

const ResetPasswordPage = (): ReactElement => {
    const user = useUser();
    const t = useTranslation();
    const setUserPassword = useMethod('setUserPassword');
    const resetPassword = useMethod('resetPassword');
    const token = useRouteParameter('token');

    const resetPasswordFormRef = useRef<HTMLElement>(null);
    const passwordId = useUniqueId();
    const passwordConfirmationId = useUniqueId();
    const passwordVerifierId = useUniqueId();
    const formLabelId = useUniqueId();

    const requiresPasswordConfirmation = useSetting('Accounts_RequirePasswordConfirmation');
    const passwordPlaceholder = String(useSetting('Accounts_PasswordPlaceholder'));
    const passwordConfirmationPlaceholder = String(useSetting('Accounts_ConfirmPasswordPlaceholder'));

    const router = useRouter();

    const changePasswordReason = getChangePasswordReason(user || {});

    const loginWithToken = useLoginWithToken();

    const {
        register,
        handleSubmit,
        setError,
        formState: { errors, isSubmitting },
        watch,
    } = useForm<{
        password: string;
        passwordConfirmation: string;
    }>({
        mode: 'onBlur',
    });

    const password = watch('password');
    const passwordIsValid = useValidatePassword(password);

    useEffect(() => {
        if (resetPasswordFormRef.current) {
            resetPasswordFormRef.current.focus();
        }
    }, []);

    const handleResetPassword = async ({ password }: { password: string }) => {
        try {
            if (token) {
                const result = await resetPassword(token, password);
                await loginWithToken(result.token);
                router.navigate('/home');
            } else {
                await setUserPassword(password);
            }
        } catch ({ error, reason }: any) {
            const _error = reason ?? error;
            setError('password', { message: String(_error) });
        }
    };

    return (
        <HorizontalTemplate>
            <Form
                tabIndex={-1}
                ref={resetPasswordFormRef}
                aria-labelledby={formLabelId}
                aria-describedby='welcomeTitle'
                onSubmit={handleSubmit(handleResetPassword)}
            >
                <Form.Header>
                    <Form.Title id={formLabelId}>{t('Reset_password')}</Form.Title>
                    <Form.Subtitle>{t(changePasswordReason)}</Form.Subtitle>
                </Form.Header>
                <Form.Container>
                    <FieldGroup>
                        <Field>
                            <FieldLabel required htmlFor={passwordId}>
                                {t('registration.component.form.password')}
                            </FieldLabel>
                            <FieldRow>
                                <PasswordInput
                                    {...register('password', {
                                        required: t('registration.component.form.requiredField'),
                                        validate: () => (!passwordIsValid ? t('Password_must_meet_the_complexity_requirements') : true),
                                    })}
                                    error={errors?.password?.message}
                                    aria-invalid={errors.password ? 'true' : 'false'}
                                    aria-required='true'
                                    id={passwordId}
                                    placeholder={passwordPlaceholder || t('Create_a_password')}
                                    aria-describedby={`${passwordVerifierId} ${passwordId}-error`}
                                />
                            </FieldRow>
                            {errors?.password && (
                                <FieldError aria-live='assertive' id={`${passwordId}-error`}>
                                    {errors.password.message}
                                </FieldError>
                            )}
                            <PasswordVerifier password={password} id={passwordVerifierId} />
                        </Field>
                        {requiresPasswordConfirmation && (
                            <Field>
                                <FieldLabel required htmlFor={passwordConfirmationId}>
                                    {t('registration.component.form.confirmPassword')}
                                </FieldLabel>
                                <FieldRow>
                                    <PasswordInput
                                        {...register('passwordConfirmation', {
                                            required: t('registration.component.form.requiredField'),
                                            deps: ['password'],
                                            validate: (val: string) => (password === val ? true : t('registration.component.form.invalidConfirmPass')),
                                        })}
                                        error={errors?.passwordConfirmation?.message}
                                        aria-required='true'
                                        aria-invalid={errors.passwordConfirmation ? 'true' : 'false'}
                                        aria-describedby={`${passwordConfirmationId}-error`}
                                        id={passwordConfirmationId}
                                        placeholder={passwordConfirmationPlaceholder || t('Confirm_password')}
                                        disabled={!passwordIsValid}
                                    />
                                </FieldRow>
                                {errors.passwordConfirmation && (
                                    <FieldError aria-live='assertive' id={`${passwordConfirmationId}-error`}>
                                        {errors.passwordConfirmation?.message}
                                    </FieldError>
                                )}
                            </Field>
                        )}
                    </FieldGroup>
                </Form.Container>
                <Form.Footer>
                    <ButtonGroup>
                        <Button primary loading={isSubmitting} type='submit'>
                            {t('Reset')}
                        </Button>
                    </ButtonGroup>
                </Form.Footer>
            </Form>
        </HorizontalTemplate>
    );
};

export default ResetPasswordPage;