mirror of
https://github.com/logto-io/logto.git
synced 2025-01-27 21:39:16 -05:00
feat(ui): add forgot password link
add forgot password link
This commit is contained in:
parent
5b2bbd801b
commit
6f58f30ed0
17 changed files with 210 additions and 62 deletions
|
@ -37,6 +37,15 @@ export default function wellKnownRoutes<T extends AnonymousRouter>(router: T, pr
|
|||
getLogtoConnectors(),
|
||||
]);
|
||||
|
||||
const forgotPassword = {
|
||||
sms: logtoConnectors.some(
|
||||
({ type, dbEntry: { enabled } }) => type === ConnectorType.Sms && enabled
|
||||
),
|
||||
email: logtoConnectors.some(
|
||||
({ type, dbEntry: { enabled } }) => type === ConnectorType.Email && enabled
|
||||
),
|
||||
};
|
||||
|
||||
// Hard code AdminConsole sign-in methods settings.
|
||||
if (interaction?.params.client_id === adminConsoleApplicationId) {
|
||||
ctx.body = {
|
||||
|
@ -48,6 +57,7 @@ export default function wellKnownRoutes<T extends AnonymousRouter>(router: T, pr
|
|||
languageInfo: signInExperience.languageInfo,
|
||||
signInMode: (await hasActiveUsers()) ? SignInMode.SignIn : SignInMode.Register,
|
||||
socialConnectors: [],
|
||||
forgotPassword,
|
||||
};
|
||||
|
||||
return next();
|
||||
|
@ -81,6 +91,7 @@ export default function wellKnownRoutes<T extends AnonymousRouter>(router: T, pr
|
|||
'demo_app.notification',
|
||||
autoDetect ? undefined : { lng: fallbackLanguage }
|
||||
),
|
||||
forgotPassword,
|
||||
};
|
||||
|
||||
return next();
|
||||
|
@ -89,14 +100,7 @@ export default function wellKnownRoutes<T extends AnonymousRouter>(router: T, pr
|
|||
ctx.body = {
|
||||
...signInExperience,
|
||||
socialConnectors,
|
||||
forgotPassword: {
|
||||
sms: logtoConnectors.some(
|
||||
({ type, dbEntry: { enabled } }) => type === ConnectorType.Sms && enabled
|
||||
),
|
||||
email: logtoConnectors.some(
|
||||
({ type, dbEntry: { enabled } }) => type === ConnectorType.Email && enabled
|
||||
),
|
||||
},
|
||||
forgotPassword,
|
||||
};
|
||||
|
||||
return next();
|
||||
|
|
19
packages/ui/src/components/ForgotPasswordLink/index.tsx
Normal file
19
packages/ui/src/components/ForgotPasswordLink/index.tsx
Normal file
|
@ -0,0 +1,19 @@
|
|||
import type { SignInIdentifier } from '@logto/schemas';
|
||||
|
||||
import TextLink from '@/components/TextLink';
|
||||
import { UserFlow } from '@/types';
|
||||
|
||||
type Props = {
|
||||
method: SignInIdentifier.Email | SignInIdentifier.Sms;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const ForgotPasswordLink = ({ method, className }: Props) => (
|
||||
<TextLink
|
||||
className={className}
|
||||
to={`/${UserFlow.forgotPassword}/${method}`}
|
||||
text="action.forgot_password"
|
||||
/>
|
||||
);
|
||||
|
||||
export default ForgotPasswordLink;
|
|
@ -14,7 +14,8 @@
|
|||
}
|
||||
|
||||
.switch {
|
||||
display: block;
|
||||
width: auto;
|
||||
align-self: start;
|
||||
}
|
||||
|
||||
.formErrors {
|
||||
|
|
|
@ -8,10 +8,16 @@
|
|||
}
|
||||
|
||||
.inputField,
|
||||
.link,
|
||||
.terms {
|
||||
margin-bottom: _.unit(4);
|
||||
}
|
||||
|
||||
.link {
|
||||
width: auto;
|
||||
align-self: start;
|
||||
}
|
||||
|
||||
.formErrors {
|
||||
margin-top: _.unit(-2);
|
||||
margin-bottom: _.unit(4);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { fireEvent, waitFor } from '@testing-library/react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
import renderWithPageContext from '@/__mocks__/RenderWithPageContext';
|
||||
import SettingsProvider from '@/__mocks__/RenderWithPageContext/SettingsProvider';
|
||||
|
@ -28,11 +29,14 @@ describe('<EmailPassword>', () => {
|
|||
|
||||
test('render with terms settings enabled', () => {
|
||||
const { queryByText } = renderWithPageContext(
|
||||
<SettingsProvider>
|
||||
<EmailPassword />
|
||||
</SettingsProvider>
|
||||
<MemoryRouter>
|
||||
<SettingsProvider>
|
||||
<EmailPassword />
|
||||
</SettingsProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
expect(queryByText('description.agree_with_terms')).not.toBeNull();
|
||||
expect(queryByText('action.forgot_password')).not.toBeNull();
|
||||
});
|
||||
|
||||
test('required inputs with error message', () => {
|
||||
|
@ -64,11 +68,13 @@ describe('<EmailPassword>', () => {
|
|||
|
||||
test('should show terms confirm modal', async () => {
|
||||
const { queryByText, getByText, container } = renderWithPageContext(
|
||||
<SettingsProvider>
|
||||
<ConfirmModalProvider>
|
||||
<EmailPassword />
|
||||
</ConfirmModalProvider>
|
||||
</SettingsProvider>
|
||||
<MemoryRouter>
|
||||
<SettingsProvider>
|
||||
<ConfirmModalProvider>
|
||||
<EmailPassword />
|
||||
</ConfirmModalProvider>
|
||||
</SettingsProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
const submitButton = getByText('action.sign_in');
|
||||
|
||||
|
@ -94,11 +100,13 @@ describe('<EmailPassword>', () => {
|
|||
|
||||
test('should show terms detail modal', async () => {
|
||||
const { getByText, queryByText, container, queryByRole } = renderWithPageContext(
|
||||
<SettingsProvider>
|
||||
<ConfirmModalProvider>
|
||||
<EmailPassword />
|
||||
</ConfirmModalProvider>
|
||||
</SettingsProvider>
|
||||
<MemoryRouter>
|
||||
<SettingsProvider>
|
||||
<ConfirmModalProvider>
|
||||
<EmailPassword />
|
||||
</ConfirmModalProvider>
|
||||
</SettingsProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
const submitButton = getByText('action.sign_in');
|
||||
|
||||
|
@ -135,9 +143,11 @@ describe('<EmailPassword>', () => {
|
|||
|
||||
test('submit form', async () => {
|
||||
const { getByText, container } = renderWithPageContext(
|
||||
<SettingsProvider>
|
||||
<EmailPassword />
|
||||
</SettingsProvider>
|
||||
<MemoryRouter>
|
||||
<SettingsProvider>
|
||||
<EmailPassword />
|
||||
</SettingsProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
const submitButton = getByText('action.sign_in');
|
||||
|
||||
|
|
|
@ -5,10 +5,12 @@ import { useTranslation } from 'react-i18next';
|
|||
|
||||
import Button from '@/components/Button';
|
||||
import ErrorMessage from '@/components/ErrorMessage';
|
||||
import ForgotPasswordLink from '@/components/ForgotPasswordLink';
|
||||
import Input, { PasswordInput } from '@/components/Input';
|
||||
import TermsOfUse from '@/containers/TermsOfUse';
|
||||
import useForm from '@/hooks/use-form';
|
||||
import usePasswordSignIn from '@/hooks/use-password-sign-in';
|
||||
import { useForgotPasswordSettings } from '@/hooks/use-sie';
|
||||
import useTerms from '@/hooks/use-terms';
|
||||
import { emailValidation, requiredValidation } from '@/utils/field-validations';
|
||||
|
||||
|
@ -34,6 +36,7 @@ const EmailPassword = ({ className, autoFocus }: Props) => {
|
|||
const { t } = useTranslation();
|
||||
const { termsValidation } = useTerms();
|
||||
const { errorMessage, clearErrorMessage, onSubmit } = usePasswordSignIn(SignInIdentifier.Email);
|
||||
const { isForgotPasswordEnabled, email } = useForgotPasswordSettings();
|
||||
|
||||
const { fieldValue, setFieldValue, register, validateForm } = useForm(defaultState);
|
||||
|
||||
|
@ -86,6 +89,13 @@ const EmailPassword = ({ className, autoFocus }: Props) => {
|
|||
{...register('password', (value) => requiredValidation('password', value))}
|
||||
/>
|
||||
|
||||
{isForgotPasswordEnabled && (
|
||||
<ForgotPasswordLink
|
||||
className={styles.link}
|
||||
method={email ? SignInIdentifier.Email : SignInIdentifier.Sms}
|
||||
/>
|
||||
)}
|
||||
|
||||
{errorMessage && <ErrorMessage className={styles.formErrors}>{errorMessage}</ErrorMessage>}
|
||||
|
||||
<TermsOfUse className={styles.terms} />
|
||||
|
|
|
@ -8,12 +8,15 @@
|
|||
}
|
||||
|
||||
.inputField,
|
||||
.link,
|
||||
.switch {
|
||||
margin-bottom: _.unit(4);
|
||||
}
|
||||
|
||||
.link,
|
||||
.switch {
|
||||
display: block;
|
||||
align-self: start;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.formErrors {
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
import type { SignInIdentifier } from '@logto/schemas';
|
||||
import { SignInIdentifier } from '@logto/schemas';
|
||||
import classNames from 'classnames';
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import Button from '@/components/Button';
|
||||
import ErrorMessage from '@/components/ErrorMessage';
|
||||
import ForgotPasswordLink from '@/components/ForgotPasswordLink';
|
||||
import { PasswordInput } from '@/components/Input';
|
||||
import useForm from '@/hooks/use-form';
|
||||
import usePasswordSignIn from '@/hooks/use-password-sign-in';
|
||||
import { useForgotPasswordSettings } from '@/hooks/use-sie';
|
||||
import { requiredValidation } from '@/utils/field-validations';
|
||||
|
||||
import PasswordlessSignInLink from './PasswordlessSignInLink';
|
||||
|
@ -42,6 +44,8 @@ const PasswordSignInForm = ({
|
|||
|
||||
const { fieldValue, register, validateForm } = useForm(defaultState);
|
||||
|
||||
const { isForgotPasswordEnabled, sms, email } = useForgotPasswordSettings();
|
||||
|
||||
const onSubmitHandler = useCallback(
|
||||
async (event?: React.FormEvent<HTMLFormElement>) => {
|
||||
event?.preventDefault();
|
||||
|
@ -69,6 +73,21 @@ const PasswordSignInForm = ({
|
|||
/>
|
||||
{errorMessage && <ErrorMessage className={styles.formErrors}>{errorMessage}</ErrorMessage>}
|
||||
|
||||
{isForgotPasswordEnabled && (
|
||||
<ForgotPasswordLink
|
||||
className={styles.link}
|
||||
method={
|
||||
method === SignInIdentifier.Email
|
||||
? email
|
||||
? SignInIdentifier.Email
|
||||
: SignInIdentifier.Sms
|
||||
: sms
|
||||
? SignInIdentifier.Sms
|
||||
: SignInIdentifier.Email
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
{hasPasswordlessButton && (
|
||||
<PasswordlessSignInLink className={styles.switch} method={method} value={value} />
|
||||
)}
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
}
|
||||
|
||||
.switch {
|
||||
display: block;
|
||||
align-self: start;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.formErrors {
|
||||
|
|
|
@ -8,10 +8,16 @@
|
|||
}
|
||||
|
||||
.inputField,
|
||||
.link,
|
||||
.terms {
|
||||
margin-bottom: _.unit(4);
|
||||
}
|
||||
|
||||
.link {
|
||||
align-self: start;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.formErrors {
|
||||
margin-top: _.unit(-2);
|
||||
margin-bottom: _.unit(4);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { fireEvent, waitFor } from '@testing-library/react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
import renderWithPageContext from '@/__mocks__/RenderWithPageContext';
|
||||
import SettingsProvider from '@/__mocks__/RenderWithPageContext/SettingsProvider';
|
||||
|
@ -35,11 +36,14 @@ describe('<PhonePassword>', () => {
|
|||
|
||||
test('render with terms settings enabled', () => {
|
||||
const { queryByText } = renderWithPageContext(
|
||||
<SettingsProvider>
|
||||
<PhonePassword />
|
||||
</SettingsProvider>
|
||||
<MemoryRouter>
|
||||
<SettingsProvider>
|
||||
<PhonePassword />
|
||||
</SettingsProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
expect(queryByText('description.agree_with_terms')).not.toBeNull();
|
||||
expect(queryByText('action.forgot_password')).not.toBeNull();
|
||||
});
|
||||
|
||||
test('required inputs with error message', () => {
|
||||
|
@ -71,11 +75,13 @@ describe('<PhonePassword>', () => {
|
|||
|
||||
test('should show terms confirm modal', async () => {
|
||||
const { queryByText, getByText, container } = renderWithPageContext(
|
||||
<SettingsProvider>
|
||||
<ConfirmModalProvider>
|
||||
<PhonePassword />
|
||||
</ConfirmModalProvider>
|
||||
</SettingsProvider>
|
||||
<MemoryRouter>
|
||||
<SettingsProvider>
|
||||
<ConfirmModalProvider>
|
||||
<PhonePassword />
|
||||
</ConfirmModalProvider>
|
||||
</SettingsProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
const submitButton = getByText('action.sign_in');
|
||||
|
||||
|
@ -101,11 +107,13 @@ describe('<PhonePassword>', () => {
|
|||
|
||||
test('should show terms detail modal', async () => {
|
||||
const { getByText, queryByText, container, queryByRole } = renderWithPageContext(
|
||||
<SettingsProvider>
|
||||
<ConfirmModalProvider>
|
||||
<PhonePassword />
|
||||
</ConfirmModalProvider>
|
||||
</SettingsProvider>
|
||||
<MemoryRouter>
|
||||
<SettingsProvider>
|
||||
<ConfirmModalProvider>
|
||||
<PhonePassword />
|
||||
</ConfirmModalProvider>
|
||||
</SettingsProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
const submitButton = getByText('action.sign_in');
|
||||
|
||||
|
@ -142,9 +150,11 @@ describe('<PhonePassword>', () => {
|
|||
|
||||
test('submit form', async () => {
|
||||
const { getByText, container } = renderWithPageContext(
|
||||
<SettingsProvider>
|
||||
<PhonePassword />
|
||||
</SettingsProvider>
|
||||
<MemoryRouter>
|
||||
<SettingsProvider>
|
||||
<PhonePassword />
|
||||
</SettingsProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
const submitButton = getByText('action.sign_in');
|
||||
|
||||
|
|
|
@ -5,11 +5,13 @@ import { useTranslation } from 'react-i18next';
|
|||
|
||||
import Button from '@/components/Button';
|
||||
import ErrorMessage from '@/components/ErrorMessage';
|
||||
import ForgotPasswordLink from '@/components/ForgotPasswordLink';
|
||||
import { PhoneInput, PasswordInput } from '@/components/Input';
|
||||
import TermsOfUse from '@/containers/TermsOfUse';
|
||||
import useForm from '@/hooks/use-form';
|
||||
import usePasswordSignIn from '@/hooks/use-password-sign-in';
|
||||
import usePhoneNumber from '@/hooks/use-phone-number';
|
||||
import { useForgotPasswordSettings } from '@/hooks/use-sie';
|
||||
import useTerms from '@/hooks/use-terms';
|
||||
import { requiredValidation } from '@/utils/field-validations';
|
||||
|
||||
|
@ -35,6 +37,7 @@ const PhonePassword = ({ className, autoFocus }: Props) => {
|
|||
const { t } = useTranslation();
|
||||
const { termsValidation } = useTerms();
|
||||
const { errorMessage, clearErrorMessage, onSubmit } = usePasswordSignIn(SignInIdentifier.Sms);
|
||||
const { isForgotPasswordEnabled, sms } = useForgotPasswordSettings();
|
||||
|
||||
const { countryList, phoneNumber, setPhoneNumber, isValidPhoneNumber } = usePhoneNumber();
|
||||
const { fieldValue, setFieldValue, register, validateForm } = useForm(defaultState);
|
||||
|
@ -108,6 +111,13 @@ const PhonePassword = ({ className, autoFocus }: Props) => {
|
|||
|
||||
{errorMessage && <ErrorMessage className={styles.formErrors}>{errorMessage}</ErrorMessage>}
|
||||
|
||||
{isForgotPasswordEnabled && (
|
||||
<ForgotPasswordLink
|
||||
className={styles.link}
|
||||
method={sms ? SignInIdentifier.Sms : SignInIdentifier.Email}
|
||||
/>
|
||||
)}
|
||||
|
||||
<TermsOfUse className={styles.terms} />
|
||||
|
||||
<Button title="action.sign_in" onClick={async () => onSubmitHandler()} />
|
||||
|
|
|
@ -8,10 +8,16 @@
|
|||
}
|
||||
|
||||
.inputField,
|
||||
.link,
|
||||
.terms {
|
||||
margin-bottom: _.unit(4);
|
||||
}
|
||||
|
||||
.link {
|
||||
align-self: start;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.formErrors {
|
||||
margin-top: _.unit(-2);
|
||||
margin-bottom: _.unit(4);
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { fireEvent, waitFor } from '@testing-library/react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
import renderWithPageContext from '@/__mocks__/RenderWithPageContext';
|
||||
import SettingsProvider from '@/__mocks__/RenderWithPageContext/SettingsProvider';
|
||||
import { mockSignInExperienceSettings } from '@/__mocks__/logto';
|
||||
import { signInWithUsername } from '@/apis/sign-in';
|
||||
import ConfirmModalProvider from '@/containers/ConfirmModalProvider';
|
||||
|
||||
|
@ -26,13 +28,28 @@ describe('<UsernameSignIn>', () => {
|
|||
expect(queryByText('action.sign_in')).not.toBeNull();
|
||||
});
|
||||
|
||||
test('render with terms settings enabled', () => {
|
||||
test('render with terms settings enabled and forgot password enabled', () => {
|
||||
const { queryByText } = renderWithPageContext(
|
||||
<SettingsProvider>
|
||||
<UsernameSignIn />
|
||||
<MemoryRouter>
|
||||
<UsernameSignIn />
|
||||
</MemoryRouter>
|
||||
</SettingsProvider>
|
||||
);
|
||||
expect(queryByText('description.agree_with_terms')).not.toBeNull();
|
||||
expect(queryByText('action.forgot_password')).not.toBeNull();
|
||||
});
|
||||
|
||||
test('render with forgot password disabled', () => {
|
||||
const { queryByText } = renderWithPageContext(
|
||||
<SettingsProvider
|
||||
settings={{ ...mockSignInExperienceSettings, forgotPassword: { sms: false, email: false } }}
|
||||
>
|
||||
<UsernameSignIn />
|
||||
</SettingsProvider>
|
||||
);
|
||||
|
||||
expect(queryByText('action.forgot_password')).toBeNull();
|
||||
});
|
||||
|
||||
test('required inputs with error message', () => {
|
||||
|
@ -63,11 +80,13 @@ describe('<UsernameSignIn>', () => {
|
|||
|
||||
test('should show terms confirm modal', async () => {
|
||||
const { queryByText, getByText, container } = renderWithPageContext(
|
||||
<SettingsProvider>
|
||||
<ConfirmModalProvider>
|
||||
<UsernameSignIn />
|
||||
</ConfirmModalProvider>
|
||||
</SettingsProvider>
|
||||
<MemoryRouter>
|
||||
<SettingsProvider>
|
||||
<ConfirmModalProvider>
|
||||
<UsernameSignIn />
|
||||
</ConfirmModalProvider>
|
||||
</SettingsProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
const submitButton = getByText('action.sign_in');
|
||||
|
||||
|
@ -93,11 +112,13 @@ describe('<UsernameSignIn>', () => {
|
|||
|
||||
test('should show terms detail modal', async () => {
|
||||
const { getByText, queryByText, container, queryByRole } = renderWithPageContext(
|
||||
<SettingsProvider>
|
||||
<ConfirmModalProvider>
|
||||
<UsernameSignIn />
|
||||
</ConfirmModalProvider>
|
||||
</SettingsProvider>
|
||||
<MemoryRouter>
|
||||
<SettingsProvider>
|
||||
<ConfirmModalProvider>
|
||||
<UsernameSignIn />
|
||||
</ConfirmModalProvider>
|
||||
</SettingsProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
const submitButton = getByText('action.sign_in');
|
||||
|
||||
|
@ -134,9 +155,11 @@ describe('<UsernameSignIn>', () => {
|
|||
|
||||
test('submit form', async () => {
|
||||
const { getByText, container } = renderWithPageContext(
|
||||
<SettingsProvider>
|
||||
<UsernameSignIn />
|
||||
</SettingsProvider>
|
||||
<MemoryRouter>
|
||||
<SettingsProvider>
|
||||
<UsernameSignIn />
|
||||
</SettingsProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
const submitButton = getByText('action.sign_in');
|
||||
|
||||
|
|
|
@ -5,10 +5,12 @@ import { useTranslation } from 'react-i18next';
|
|||
|
||||
import Button from '@/components/Button';
|
||||
import ErrorMessage from '@/components/ErrorMessage';
|
||||
import ForgotPasswordLink from '@/components/ForgotPasswordLink';
|
||||
import Input, { PasswordInput } from '@/components/Input';
|
||||
import TermsOfUse from '@/containers/TermsOfUse';
|
||||
import useForm from '@/hooks/use-form';
|
||||
import usePasswordSignIn from '@/hooks/use-password-sign-in';
|
||||
import { useForgotPasswordSettings } from '@/hooks/use-sie';
|
||||
import useTerms from '@/hooks/use-terms';
|
||||
import { requiredValidation } from '@/utils/field-validations';
|
||||
|
||||
|
@ -33,6 +35,7 @@ const defaultState: FieldState = {
|
|||
const UsernameSignIn = ({ className, autoFocus }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const { termsValidation } = useTerms();
|
||||
const { isForgotPasswordEnabled, email } = useForgotPasswordSettings();
|
||||
const { errorMessage, clearErrorMessage, onSubmit } = usePasswordSignIn(
|
||||
SignInIdentifier.Username
|
||||
);
|
||||
|
@ -87,6 +90,13 @@ const UsernameSignIn = ({ className, autoFocus }: Props) => {
|
|||
/>
|
||||
{errorMessage && <ErrorMessage className={styles.formErrors}>{errorMessage}</ErrorMessage>}
|
||||
|
||||
{isForgotPasswordEnabled && (
|
||||
<ForgotPasswordLink
|
||||
className={styles.link}
|
||||
method={email ? SignInIdentifier.Email : SignInIdentifier.Sms}
|
||||
/>
|
||||
)}
|
||||
|
||||
<TermsOfUse className={styles.terms} />
|
||||
|
||||
<Button title="action.sign_in" onClick={async () => onSubmitHandler()} />
|
||||
|
|
|
@ -15,3 +15,13 @@ export const useSieMethods = () => {
|
|||
forgotPassword: experienceSettings?.forgotPassword,
|
||||
};
|
||||
};
|
||||
|
||||
export const useForgotPasswordSettings = () => {
|
||||
const { experienceSettings } = useContext(PageContext);
|
||||
const { forgotPassword } = experienceSettings ?? {};
|
||||
|
||||
return {
|
||||
isForgotPasswordEnabled: Boolean(forgotPassword?.email ?? forgotPassword?.sms),
|
||||
...forgotPassword,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -5,7 +5,7 @@ import { is } from 'superstruct';
|
|||
import SecondaryPageWrapper from '@/components/SecondaryPageWrapper';
|
||||
import { EmailResetPassword } from '@/containers/EmailForm';
|
||||
import { SmsResetPassword } from '@/containers/PhoneForm';
|
||||
import { useSieMethods } from '@/hooks/use-sie';
|
||||
import { useForgotPasswordSettings } from '@/hooks/use-sie';
|
||||
import ErrorPage from '@/pages/ErrorPage';
|
||||
import { passcodeMethodGuard } from '@/types/guard';
|
||||
|
||||
|
@ -15,14 +15,14 @@ type Props = {
|
|||
|
||||
const ForgotPassword = () => {
|
||||
const { method = '' } = useParams<Props>();
|
||||
const { forgotPassword } = useSieMethods();
|
||||
const forgotPassword = useForgotPasswordSettings();
|
||||
|
||||
if (!is(method, passcodeMethodGuard)) {
|
||||
return <ErrorPage />;
|
||||
}
|
||||
|
||||
// Forgot password with target identifier method is not supported
|
||||
if (!forgotPassword?.[method]) {
|
||||
if (!forgotPassword[method]) {
|
||||
return <ErrorPage />;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue