mirror of
https://github.com/logto-io/logto.git
synced 2025-02-03 21:48:55 -05:00
refactor(ui): replace termsOfUse Component with Container (#610)
* refactor(ui): refactor terms of use refactor terms of use * fix(ui): fix terms modal fix terms mdoal * refactor(ui): replace termsOfUse Component with Container replace termsOfUse Component With Container
This commit is contained in:
parent
3d8c3af5bd
commit
666c5d8b8c
12 changed files with 107 additions and 126 deletions
|
@ -3,12 +3,14 @@ import { useContext, useEffect, ReactElement } from 'react';
|
|||
import { PageContext } from '@/hooks/use-page-context';
|
||||
import { SignInExperienceSettings } from '@/types';
|
||||
|
||||
import { mockSignInExperienceSettings } from '../logto';
|
||||
|
||||
type Props = {
|
||||
settings: SignInExperienceSettings;
|
||||
settings?: SignInExperienceSettings;
|
||||
children: ReactElement;
|
||||
};
|
||||
|
||||
const SettingsProvider = ({ settings, children }: Props) => {
|
||||
const SettingsProvider = ({ settings = mockSignInExperienceSettings, children }: Props) => {
|
||||
const { setExperienceSettings } = useContext(PageContext);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -2,6 +2,7 @@ import { fireEvent, waitFor } from '@testing-library/react';
|
|||
import React from 'react';
|
||||
|
||||
import renderWithPageContext from '@/__mocks__/RenderWithPageContext';
|
||||
import SettingsProvider from '@/__mocks__/RenderWithPageContext/SettingsProvider';
|
||||
import { register } from '@/apis/register';
|
||||
|
||||
import CreateAccount from '.';
|
||||
|
@ -15,6 +16,14 @@ describe('<CreateAccount/>', () => {
|
|||
expect(container.querySelector('input[name="password"]')).not.toBeNull();
|
||||
expect(container.querySelector('input[name="confirm_password"]')).not.toBeNull();
|
||||
expect(queryByText('action.create')).not.toBeNull();
|
||||
});
|
||||
|
||||
test('render with terms settings enabled', () => {
|
||||
const { queryByText } = renderWithPageContext(
|
||||
<SettingsProvider>
|
||||
<CreateAccount />
|
||||
</SettingsProvider>
|
||||
);
|
||||
expect(queryByText('description.terms_of_use')).not.toBeNull();
|
||||
});
|
||||
|
||||
|
@ -131,8 +140,12 @@ describe('<CreateAccount/>', () => {
|
|||
expect(queryByText('passwords_do_not_match')).toBeNull();
|
||||
});
|
||||
|
||||
test('submit form properly', async () => {
|
||||
const { getByText, container } = renderWithPageContext(<CreateAccount />);
|
||||
test('submit form properly with terms settings enabled', async () => {
|
||||
const { getByText, container } = renderWithPageContext(
|
||||
<SettingsProvider>
|
||||
<CreateAccount />
|
||||
</SettingsProvider>
|
||||
);
|
||||
const submitButton = getByText('action.create');
|
||||
const passwordInput = container.querySelector('input[name="password"]');
|
||||
const confirmPasswordInput = container.querySelector('input[name="confirm_password"]');
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
* TODO:
|
||||
* 1. API redesign handle api error and loading status globally in PageContext
|
||||
* 2. Input field validation, should move the validation rule to the input field scope
|
||||
* 4. Read terms of use settings from SignInExperience Settings
|
||||
*/
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
@ -14,9 +13,10 @@ import Button from '@/components/Button';
|
|||
import { ErrorType } from '@/components/ErrorMessage';
|
||||
import Input from '@/components/Input';
|
||||
import PasswordInput from '@/components/Input/PasswordInput';
|
||||
import TermsOfUse from '@/components/TermsOfUse';
|
||||
import TermsOfUse from '@/containers/TermsOfUse';
|
||||
import useApi from '@/hooks/use-api';
|
||||
import { PageContext } from '@/hooks/use-page-context';
|
||||
import useTerms from '@/hooks/use-terms';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
|
@ -24,7 +24,6 @@ type FieldState = {
|
|||
username: string;
|
||||
password: string;
|
||||
confirmPassword: string;
|
||||
termsAgreement: boolean;
|
||||
};
|
||||
|
||||
type ErrorState = {
|
||||
|
@ -43,7 +42,6 @@ const defaultState = {
|
|||
username: '',
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
termsAgreement: false,
|
||||
};
|
||||
|
||||
const usernameRegx = /^[A-Z_a-z-][\w-]*$/;
|
||||
|
@ -52,6 +50,7 @@ const CreateAccount = ({ className }: Props) => {
|
|||
const { t, i18n } = useTranslation(undefined, { keyPrefix: 'main_flow' });
|
||||
const [fieldState, setFieldState] = useState<FieldState>(defaultState);
|
||||
const [fieldErrors, setFieldErrors] = useState<ErrorState>({});
|
||||
const { termsValidation } = useTerms();
|
||||
|
||||
const { setToast } = useContext(PageContext);
|
||||
|
||||
|
@ -86,11 +85,6 @@ const CreateAccount = ({ className }: Props) => {
|
|||
return { code: 'passwords_do_not_match' };
|
||||
}
|
||||
},
|
||||
termsAgreement: ({ termsAgreement }) => {
|
||||
if (!termsAgreement) {
|
||||
return 'agree_terms_required';
|
||||
}
|
||||
},
|
||||
}),
|
||||
[t]
|
||||
);
|
||||
|
@ -118,19 +112,12 @@ const CreateAccount = ({ className }: Props) => {
|
|||
return;
|
||||
}
|
||||
|
||||
const termsAgreementError = validations.termsAgreement?.(fieldState);
|
||||
|
||||
if (termsAgreementError) {
|
||||
setFieldErrors((previous) => ({
|
||||
...previous,
|
||||
termsAgreement: termsAgreementError,
|
||||
}));
|
||||
|
||||
if (!termsValidation()) {
|
||||
return;
|
||||
}
|
||||
|
||||
void asyncRegister(fieldState.username, fieldState.password);
|
||||
}, [fieldState, validations, asyncRegister]);
|
||||
}, [validations, fieldState, termsValidation, asyncRegister]);
|
||||
|
||||
useEffect(() => {
|
||||
if (result?.redirectTo) {
|
||||
|
@ -208,15 +195,7 @@ const CreateAccount = ({ className }: Props) => {
|
|||
}
|
||||
}}
|
||||
/>
|
||||
<TermsOfUse
|
||||
name="termsAgreement"
|
||||
className={styles.terms}
|
||||
termsUrl="/"
|
||||
isChecked={fieldState.termsAgreement}
|
||||
onChange={(checked) => {
|
||||
setFieldState((state) => ({ ...state, termsAgreement: checked }));
|
||||
}}
|
||||
/>
|
||||
<TermsOfUse className={styles.terms} />
|
||||
|
||||
<Button onClick={onSubmitHandler}>{t('action.create')}</Button>
|
||||
</form>
|
||||
|
|
|
@ -3,6 +3,7 @@ import React from 'react';
|
|||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
import renderWithPageContext from '@/__mocks__/RenderWithPageContext';
|
||||
import SettingsProvider from '@/__mocks__/RenderWithPageContext/SettingsProvider';
|
||||
import { sendRegisterEmailPasscode } from '@/apis/register';
|
||||
import { sendSignInEmailPasscode } from '@/apis/sign-in';
|
||||
|
||||
|
@ -24,6 +25,16 @@ describe('<EmailPasswordless/>', () => {
|
|||
);
|
||||
expect(container.querySelector('input[name="email"]')).not.toBeNull();
|
||||
expect(queryByText('action.continue')).not.toBeNull();
|
||||
});
|
||||
|
||||
test('render with terms settings enabled', () => {
|
||||
const { queryByText } = renderWithPageContext(
|
||||
<MemoryRouter>
|
||||
<SettingsProvider>
|
||||
<EmailPasswordless type="sign-in" />
|
||||
</SettingsProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
expect(queryByText('description.terms_of_use')).not.toBeNull();
|
||||
});
|
||||
|
||||
|
@ -53,7 +64,9 @@ describe('<EmailPasswordless/>', () => {
|
|||
test('should call sign-in method properly', async () => {
|
||||
const { container, getByText } = renderWithPageContext(
|
||||
<MemoryRouter>
|
||||
<EmailPasswordless type="sign-in" />
|
||||
<SettingsProvider>
|
||||
<EmailPasswordless type="sign-in" />
|
||||
</SettingsProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
const emailInput = container.querySelector('input[name="email"]');
|
||||
|
@ -76,7 +89,9 @@ describe('<EmailPasswordless/>', () => {
|
|||
test('should call register method properly', async () => {
|
||||
const { container, getByText } = renderWithPageContext(
|
||||
<MemoryRouter>
|
||||
<EmailPasswordless type="register" />
|
||||
<SettingsProvider>
|
||||
<EmailPasswordless type="register" />
|
||||
</SettingsProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
const emailInput = container.querySelector('input[name="email"]');
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
* TODO:
|
||||
* 1. API redesign handle api error and loading status globally in PageContext
|
||||
* 2. Input field validation, should move the validation rule to the input field scope
|
||||
* 4. Read terms of use settings from SignInExperience Settings
|
||||
*/
|
||||
import classNames from 'classnames';
|
||||
import React, { useState, useCallback, useMemo, useEffect, useContext } from 'react';
|
||||
|
@ -13,9 +12,10 @@ import { getSendPasscodeApi } from '@/apis/utils';
|
|||
import Button from '@/components/Button';
|
||||
import { ErrorType } from '@/components/ErrorMessage';
|
||||
import Input from '@/components/Input';
|
||||
import TermsOfUse from '@/components/TermsOfUse';
|
||||
import TermsOfUse from '@/containers/TermsOfUse';
|
||||
import useApi from '@/hooks/use-api';
|
||||
import { PageContext } from '@/hooks/use-page-context';
|
||||
import useTerms from '@/hooks/use-terms';
|
||||
import { UserFlow } from '@/types';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
@ -27,7 +27,6 @@ type Props = {
|
|||
|
||||
type FieldState = {
|
||||
email: string;
|
||||
termsAgreement: boolean;
|
||||
};
|
||||
|
||||
type ErrorState = {
|
||||
|
@ -38,7 +37,7 @@ type FieldValidations = {
|
|||
[key in keyof FieldState]: (state: FieldState) => ErrorType | undefined;
|
||||
};
|
||||
|
||||
const defaultState: FieldState = { email: '', termsAgreement: false };
|
||||
const defaultState: FieldState = { email: '' };
|
||||
|
||||
const emailRegEx = /^\S+@\S+\.\S+$/;
|
||||
|
||||
|
@ -48,6 +47,7 @@ const EmailPasswordless = ({ type, className }: Props) => {
|
|||
const [fieldErrors, setFieldErrors] = useState<ErrorState>({});
|
||||
const { setToast } = useContext(PageContext);
|
||||
const navigate = useNavigate();
|
||||
const { termsValidation } = useTerms();
|
||||
|
||||
const sendPasscode = getSendPasscodeApi(type, 'email');
|
||||
|
||||
|
@ -60,11 +60,6 @@ const EmailPasswordless = ({ type, className }: Props) => {
|
|||
return 'invalid_email';
|
||||
}
|
||||
},
|
||||
termsAgreement: ({ termsAgreement }) => {
|
||||
if (!termsAgreement) {
|
||||
return 'agree_terms_required';
|
||||
}
|
||||
},
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
@ -78,16 +73,12 @@ const EmailPasswordless = ({ type, className }: Props) => {
|
|||
return;
|
||||
}
|
||||
|
||||
const termsAgreementError = validations.termsAgreement(fieldState);
|
||||
|
||||
if (termsAgreementError) {
|
||||
setFieldErrors((previous) => ({ ...previous, termsAgreement: termsAgreementError }));
|
||||
|
||||
if (!termsValidation()) {
|
||||
return;
|
||||
}
|
||||
|
||||
void asyncSendPasscode(fieldState.email);
|
||||
}, [validations, fieldState, asyncSendPasscode]);
|
||||
}, [validations, fieldState, termsValidation, asyncSendPasscode]);
|
||||
|
||||
useEffect(() => {
|
||||
if (result) {
|
||||
|
@ -137,15 +128,7 @@ const EmailPasswordless = ({ type, className }: Props) => {
|
|||
}}
|
||||
/>
|
||||
|
||||
<TermsOfUse
|
||||
name="termsAgreement"
|
||||
className={styles.terms}
|
||||
termsUrl="/"
|
||||
isChecked={fieldState.termsAgreement}
|
||||
onChange={(checked) => {
|
||||
setFieldState((state) => ({ ...state, termsAgreement: checked }));
|
||||
}}
|
||||
/>
|
||||
<TermsOfUse className={styles.terms} />
|
||||
|
||||
<Button onClick={onSubmitHandler}>{t('action.continue')}</Button>
|
||||
</form>
|
||||
|
|
|
@ -3,6 +3,7 @@ import React from 'react';
|
|||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
import renderWithPageContext from '@/__mocks__/RenderWithPageContext';
|
||||
import SettingsProvider from '@/__mocks__/RenderWithPageContext/SettingsProvider';
|
||||
import { sendRegisterSmsPasscode } from '@/apis/register';
|
||||
import { sendSignInSmsPasscode } from '@/apis/sign-in';
|
||||
import { defaultCountryCallingCode } from '@/hooks/use-phone-number';
|
||||
|
@ -27,6 +28,16 @@ describe('<PhonePasswordless/>', () => {
|
|||
);
|
||||
expect(container.querySelector('input[name="phone"]')).not.toBeNull();
|
||||
expect(queryByText('action.continue')).not.toBeNull();
|
||||
});
|
||||
|
||||
test('render with terms settings enabled', () => {
|
||||
const { queryByText } = renderWithPageContext(
|
||||
<MemoryRouter>
|
||||
<SettingsProvider>
|
||||
<PhonePasswordless type="sign-in" />
|
||||
</SettingsProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
expect(queryByText('description.terms_of_use')).not.toBeNull();
|
||||
});
|
||||
|
||||
|
@ -56,7 +67,9 @@ describe('<PhonePasswordless/>', () => {
|
|||
test('should call sign-in method properly', async () => {
|
||||
const { container, getByText } = renderWithPageContext(
|
||||
<MemoryRouter>
|
||||
<PhonePasswordless type="sign-in" />
|
||||
<SettingsProvider>
|
||||
<PhonePasswordless type="sign-in" />
|
||||
</SettingsProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
const phoneInput = container.querySelector('input[name="phone"]');
|
||||
|
@ -79,7 +92,9 @@ describe('<PhonePasswordless/>', () => {
|
|||
test('should call register method properly', async () => {
|
||||
const { container, getByText } = renderWithPageContext(
|
||||
<MemoryRouter>
|
||||
<PhonePasswordless type="register" />
|
||||
<SettingsProvider>
|
||||
<PhonePasswordless type="register" />
|
||||
</SettingsProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
const phoneInput = container.querySelector('input[name="phone"]');
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
* TODO:
|
||||
* 1. API redesign handle api error and loading status globally in PageContext
|
||||
* 2. Input field validation, should move the validation rule to the input field scope
|
||||
* 4. Read terms of use settings from SignInExperience Settings
|
||||
*/
|
||||
import classNames from 'classnames';
|
||||
import React, { useState, useCallback, useMemo, useEffect, useContext } from 'react';
|
||||
|
@ -13,10 +12,11 @@ import { getSendPasscodeApi } from '@/apis/utils';
|
|||
import Button from '@/components/Button';
|
||||
import { ErrorType } from '@/components/ErrorMessage';
|
||||
import PhoneInput from '@/components/Input/PhoneInput';
|
||||
import TermsOfUse from '@/components/TermsOfUse';
|
||||
import TermsOfUse from '@/containers/TermsOfUse';
|
||||
import useApi from '@/hooks/use-api';
|
||||
import { PageContext } from '@/hooks/use-page-context';
|
||||
import usePhoneNumber, { countryList } from '@/hooks/use-phone-number';
|
||||
import useTerms from '@/hooks/use-terms';
|
||||
import { UserFlow } from '@/types';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
@ -28,7 +28,6 @@ type Props = {
|
|||
|
||||
type FieldState = {
|
||||
phone: string;
|
||||
termsAgreement: boolean;
|
||||
};
|
||||
|
||||
type ErrorState = {
|
||||
|
@ -39,7 +38,7 @@ type FieldValidations = {
|
|||
[key in keyof FieldState]: (state: FieldState) => ErrorType | undefined;
|
||||
};
|
||||
|
||||
const defaultState: FieldState = { phone: '', termsAgreement: false };
|
||||
const defaultState: FieldState = { phone: '' };
|
||||
|
||||
const PhonePasswordless = ({ type, className }: Props) => {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'main_flow' });
|
||||
|
@ -47,6 +46,7 @@ const PhonePasswordless = ({ type, className }: Props) => {
|
|||
const [fieldErrors, setFieldErrors] = useState<ErrorState>({});
|
||||
const { setToast } = useContext(PageContext);
|
||||
const navigate = useNavigate();
|
||||
const { termsValidation } = useTerms();
|
||||
|
||||
const { phoneNumber, setPhoneNumber, isValidPhoneNumber } = usePhoneNumber();
|
||||
|
||||
|
@ -60,11 +60,6 @@ const PhonePasswordless = ({ type, className }: Props) => {
|
|||
return 'invalid_phone';
|
||||
}
|
||||
},
|
||||
termsAgreement: ({ termsAgreement }) => {
|
||||
if (!termsAgreement) {
|
||||
return 'agree_terms_required';
|
||||
}
|
||||
},
|
||||
}),
|
||||
[isValidPhoneNumber]
|
||||
);
|
||||
|
@ -78,16 +73,12 @@ const PhonePasswordless = ({ type, className }: Props) => {
|
|||
return;
|
||||
}
|
||||
|
||||
const termsAgreementError = validations.termsAgreement(fieldState);
|
||||
|
||||
if (termsAgreementError) {
|
||||
setFieldErrors((previous) => ({ ...previous, termsAgreement: termsAgreementError }));
|
||||
|
||||
if (!termsValidation()) {
|
||||
return;
|
||||
}
|
||||
|
||||
void asyncSendPasscode(fieldState.phone);
|
||||
}, [validations, fieldState, asyncSendPasscode]);
|
||||
}, [validations, fieldState, termsValidation, asyncSendPasscode]);
|
||||
|
||||
useEffect(() => {
|
||||
setFieldState((previous) => ({
|
||||
|
@ -97,8 +88,6 @@ const PhonePasswordless = ({ type, className }: Props) => {
|
|||
}, [phoneNumber]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log(result);
|
||||
|
||||
if (result) {
|
||||
navigate(`/${type}/sms/passcode-validation`, { state: { phone: fieldState.phone } });
|
||||
}
|
||||
|
@ -140,15 +129,7 @@ const PhonePasswordless = ({ type, className }: Props) => {
|
|||
setPhoneNumber((previous) => ({ ...previous, ...data }));
|
||||
}}
|
||||
/>
|
||||
<TermsOfUse
|
||||
name="termsAgreement"
|
||||
className={styles.terms}
|
||||
termsUrl="/"
|
||||
isChecked={fieldState.termsAgreement}
|
||||
onChange={(checked) => {
|
||||
setFieldState((state) => ({ ...state, termsAgreement: checked }));
|
||||
}}
|
||||
/>
|
||||
<TermsOfUse className={styles.terms} />
|
||||
|
||||
<Button onClick={onSubmitHandler}>{t('action.continue')}</Button>
|
||||
</form>
|
||||
|
|
|
@ -2,7 +2,6 @@ import React from 'react';
|
|||
|
||||
import renderWithPageContext from '@/__mocks__/RenderWithPageContext';
|
||||
import SettingsProvider from '@/__mocks__/RenderWithPageContext/SettingsProvider';
|
||||
import { mockSignInExperienceSettings } from '@/__mocks__/logto';
|
||||
|
||||
import TermsOfUse from '.';
|
||||
|
||||
|
@ -14,7 +13,7 @@ describe('TermsOfUse Container', () => {
|
|||
|
||||
it('render with settings', async () => {
|
||||
const { queryByText } = renderWithPageContext(
|
||||
<SettingsProvider settings={mockSignInExperienceSettings}>
|
||||
<SettingsProvider>
|
||||
<TermsOfUse />
|
||||
</SettingsProvider>
|
||||
);
|
||||
|
|
|
@ -2,6 +2,7 @@ import { fireEvent, waitFor } from '@testing-library/react';
|
|||
import React from 'react';
|
||||
|
||||
import renderWithPageContext from '@/__mocks__/RenderWithPageContext';
|
||||
import SettingsProvider from '@/__mocks__/RenderWithPageContext/SettingsProvider';
|
||||
import { signInBasic } from '@/apis/sign-in';
|
||||
|
||||
import UsernameSignin from '.';
|
||||
|
@ -14,6 +15,14 @@ describe('<UsernameSignin>', () => {
|
|||
expect(container.querySelector('input[name="username"]')).not.toBeNull();
|
||||
expect(container.querySelector('input[name="password"]')).not.toBeNull();
|
||||
expect(queryByText('action.sign_in')).not.toBeNull();
|
||||
});
|
||||
|
||||
test('render with terms settings enabled', () => {
|
||||
const { queryByText } = renderWithPageContext(
|
||||
<SettingsProvider>
|
||||
<UsernameSignin />
|
||||
</SettingsProvider>
|
||||
);
|
||||
expect(queryByText('description.agree_with_terms')).not.toBeNull();
|
||||
});
|
||||
|
||||
|
@ -41,15 +50,15 @@ describe('<UsernameSignin>', () => {
|
|||
fireEvent.change(passwordInput, { target: { value: 'password' } });
|
||||
}
|
||||
|
||||
fireEvent.click(submitButton);
|
||||
|
||||
expect(queryByText('required')).toBeNull();
|
||||
|
||||
expect(signInBasic).not.toBeCalled();
|
||||
});
|
||||
|
||||
test('submit form', async () => {
|
||||
const { getByText, container } = renderWithPageContext(<UsernameSignin />);
|
||||
const { getByText, container } = renderWithPageContext(
|
||||
<SettingsProvider>
|
||||
<UsernameSignin />
|
||||
</SettingsProvider>
|
||||
);
|
||||
const submitButton = getByText('action.sign_in');
|
||||
|
||||
const usernameInput = container.querySelector('input[name="username"]');
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
* TODO:
|
||||
* 1. API redesign handle api error and loading status globally in PageContext
|
||||
* 2. Input field validation, should move the validation rule to the input field scope
|
||||
* 4. Read terms of use settings from SignInExperience Settings
|
||||
*/
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
@ -14,16 +13,16 @@ import Button from '@/components/Button';
|
|||
import { ErrorType } from '@/components/ErrorMessage';
|
||||
import Input from '@/components/Input';
|
||||
import PasswordInput from '@/components/Input/PasswordInput';
|
||||
import TermsOfUse from '@/components/TermsOfUse';
|
||||
import TermsOfUse from '@/containers/TermsOfUse';
|
||||
import useApi from '@/hooks/use-api';
|
||||
import { PageContext } from '@/hooks/use-page-context';
|
||||
import useTerms from '@/hooks/use-terms';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
type FieldState = {
|
||||
username: string;
|
||||
password: string;
|
||||
termsAgreement: boolean;
|
||||
};
|
||||
|
||||
type ErrorState = {
|
||||
|
@ -41,17 +40,15 @@ type Props = {
|
|||
const defaultState: FieldState = {
|
||||
username: '',
|
||||
password: '',
|
||||
termsAgreement: false,
|
||||
};
|
||||
|
||||
const UsernameSignin = ({ className }: Props) => {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'main_flow' });
|
||||
const [fieldState, setFieldState] = useState<FieldState>(defaultState);
|
||||
const [fieldErrors, setFieldErrors] = useState<ErrorState>({});
|
||||
|
||||
const { setToast } = useContext(PageContext);
|
||||
|
||||
const { error, result, run: asyncSignInBasic } = useApi(signInBasic);
|
||||
const { termsValidation } = useTerms();
|
||||
|
||||
const validations = useMemo<FieldValidations>(
|
||||
() => ({
|
||||
|
@ -65,11 +62,6 @@ const UsernameSignin = ({ className }: Props) => {
|
|||
return { code: 'required', data: { field: t('input.password') } };
|
||||
}
|
||||
},
|
||||
termsAgreement: ({ termsAgreement }) => {
|
||||
if (!termsAgreement) {
|
||||
return 'agree_terms_required';
|
||||
}
|
||||
},
|
||||
}),
|
||||
[t]
|
||||
);
|
||||
|
@ -89,19 +81,12 @@ const UsernameSignin = ({ className }: Props) => {
|
|||
return;
|
||||
}
|
||||
|
||||
const termsAgreementError = validations.termsAgreement?.(fieldState);
|
||||
|
||||
if (termsAgreementError) {
|
||||
setFieldErrors((previous) => ({
|
||||
...previous,
|
||||
termsAgreement: termsAgreementError,
|
||||
}));
|
||||
|
||||
if (!termsValidation()) {
|
||||
return;
|
||||
}
|
||||
|
||||
void asyncSignInBasic(fieldState.username, fieldState.password);
|
||||
}, [validations, fieldState, asyncSignInBasic]);
|
||||
}, [validations, fieldState, asyncSignInBasic, termsValidation]);
|
||||
|
||||
useEffect(() => {
|
||||
if (result?.redirectTo) {
|
||||
|
@ -165,15 +150,7 @@ const UsernameSignin = ({ className }: Props) => {
|
|||
}}
|
||||
/>
|
||||
|
||||
<TermsOfUse
|
||||
name="termsAgreement"
|
||||
className={styles.terms}
|
||||
termsUrl="/"
|
||||
isChecked={fieldState.termsAgreement}
|
||||
onChange={(checked) => {
|
||||
setFieldState((state) => ({ ...state, termsAgreement: checked }));
|
||||
}}
|
||||
/>
|
||||
<TermsOfUse className={styles.terms} />
|
||||
|
||||
<Button onClick={onSubmitHandler}>{t('action.sign_in')}</Button>
|
||||
</form>
|
||||
|
|
|
@ -6,6 +6,7 @@ import { generateRandomString, parseQueryParameters } from '@/utils';
|
|||
|
||||
import useApi from './use-api';
|
||||
import { PageContext } from './use-page-context';
|
||||
import useTerms from './use-terms';
|
||||
|
||||
/**
|
||||
* Social Connector State Utility Methods
|
||||
|
@ -65,6 +66,7 @@ const isNativeWebview = () => {
|
|||
|
||||
const useSocial = () => {
|
||||
const { setToast } = useContext(PageContext);
|
||||
const { termsValidation } = useTerms();
|
||||
const parameters = useParams();
|
||||
|
||||
const { result: invokeSocialSignInResult, run: asyncInvokeSocialSignIn } =
|
||||
|
@ -74,6 +76,10 @@ const useSocial = () => {
|
|||
|
||||
const invokeSocialSignInHandler = useCallback(
|
||||
async (connectorId: string) => {
|
||||
if (!termsValidation()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const state = generateState();
|
||||
storeState(state, connectorId);
|
||||
|
||||
|
@ -81,7 +87,7 @@ const useSocial = () => {
|
|||
|
||||
return asyncInvokeSocialSignIn(connectorId, state, `${origin}/callback/${connectorId}`);
|
||||
},
|
||||
[asyncInvokeSocialSignIn]
|
||||
[asyncInvokeSocialSignIn, termsValidation]
|
||||
);
|
||||
|
||||
const signInWithSocialHandler = useCallback(
|
||||
|
@ -166,7 +172,7 @@ const useSocial = () => {
|
|||
}
|
||||
}, [signInWithSocialResult]);
|
||||
|
||||
// SignIn Callback Page Handler
|
||||
// Social Sign-In Callback Handler
|
||||
useEffect(() => {
|
||||
if (!location.pathname.includes('/sign-in/callback') || !parameters.connector) {
|
||||
return;
|
||||
|
|
|
@ -12,12 +12,14 @@ const useTerms = () => {
|
|||
} = useContext(PageContext);
|
||||
|
||||
const termsValidation = useCallback(() => {
|
||||
if (termsAgreement) {
|
||||
return;
|
||||
if (termsAgreement || !experienceSettings?.termsOfUse.enabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
setShowTermsModal(true);
|
||||
}, [setShowTermsModal, termsAgreement]);
|
||||
|
||||
return false;
|
||||
}, [experienceSettings, termsAgreement, setShowTermsModal]);
|
||||
|
||||
return {
|
||||
termsSettings: experienceSettings?.termsOfUse,
|
||||
|
|
Loading…
Add table
Reference in a new issue