Skip to content

Commit 5cf261d

Browse files
authored
feat(console): add connector check for forgot password methods (#7635)
1 parent e4182c6 commit 5cf261d

File tree

4 files changed

+91
-26
lines changed

4 files changed

+91
-26
lines changed

packages/console/src/pages/SignInExperience/PageContent/SignUpAndSignIn/SignInForm/ForgotPasswordMethodEditBox/VerificationMethodItem.tsx

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,46 @@
1-
import type { ForgotPasswordMethod } from '@logto/schemas';
1+
import type { ConnectorType, ForgotPasswordMethod } from '@logto/schemas';
2+
import classNames from 'classnames';
23
import { useTranslation } from 'react-i18next';
34

45
import Draggable from '@/assets/icons/draggable.svg?react';
56
import Minus from '@/assets/icons/minus.svg?react';
67
import IconButton from '@/ds-components/IconButton';
78

9+
import ConnectorSetupWarning from '../../components/ConnectorSetupWarning';
10+
811
import styles from './index.module.scss';
912
import { forgotPasswordMethodPhrase } from './utils';
1013

1114
type Props = {
1215
readonly method: ForgotPasswordMethod;
16+
readonly requiredConnectors: ConnectorType[];
17+
readonly hasError?: boolean;
18+
readonly errorMessage?: string;
1319
readonly onRemove: () => void;
1420
};
1521

16-
function VerificationMethodItem({ method, onRemove }: Props) {
22+
function VerificationMethodItem({
23+
method,
24+
requiredConnectors,
25+
hasError,
26+
errorMessage,
27+
onRemove,
28+
}: Props) {
1729
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
1830

1931
return (
20-
<div className={styles.methodItem}>
21-
<div className={styles.methodContent}>
22-
<Draggable className={styles.draggableIcon} />
23-
<span className={styles.methodLabel}>{t(forgotPasswordMethodPhrase[method])}</span>
32+
<div>
33+
<div className={styles.methodItem}>
34+
<div className={classNames(styles.methodContent, hasError && styles.error)}>
35+
<Draggable className={styles.draggableIcon} />
36+
<span className={styles.methodLabel}>{t(forgotPasswordMethodPhrase[method])}</span>
37+
</div>
38+
<IconButton onClick={onRemove}>
39+
<Minus />
40+
</IconButton>
2441
</div>
25-
<IconButton onClick={onRemove}>
26-
<Minus />
27-
</IconButton>
42+
{errorMessage && <div className={styles.errorMessage}>{errorMessage}</div>}
43+
<ConnectorSetupWarning requiredConnectors={requiredConnectors} />
2844
</div>
2945
);
3046
}

packages/console/src/pages/SignInExperience/PageContent/SignUpAndSignIn/SignInForm/ForgotPasswordMethodEditBox/index.module.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,9 @@
3636
.plusIcon {
3737
color: var(--color-text-secondary);
3838
}
39+
40+
.errorMessage {
41+
margin-top: _.unit(1);
42+
color: var(--color-error);
43+
font: var(--font-body-2);
44+
}

packages/console/src/pages/SignInExperience/PageContent/SignUpAndSignIn/SignInForm/ForgotPasswordMethodEditBox/index.tsx

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ import ActionMenu from '@/ds-components/ActionMenu';
77
import { DragDropProvider, DraggableItem } from '@/ds-components/DragDrop';
88
import { DropdownItem } from '@/ds-components/Dropdown';
99
import FormField from '@/ds-components/FormField';
10+
import useEnabledConnectorTypes from '@/hooks/use-enabled-connector-types';
1011

1112
import type { SignInExperienceForm } from '../../../../types';
1213
import FormFieldDescription from '../../../components/FormFieldDescription';
14+
import { getForgotPasswordMethodsRequiredConnectors } from '../../utils';
1315

1416
import VerificationMethodItem from './VerificationMethodItem';
1517
import styles from './index.module.scss';
@@ -18,6 +20,7 @@ import { forgotPasswordMethodPhrase } from './utils';
1820
function ForgotPasswordMethodEditBox() {
1921
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
2022
const { control } = useFormContext<SignInExperienceForm>();
23+
const { isConnectorTypeEnabled } = useEnabledConnectorTypes();
2124

2225
return (
2326
<FormField title="sign_in_exp.sign_up_and_sign_in.sign_in.forgot_password_verification_method">
@@ -28,8 +31,17 @@ function ForgotPasswordMethodEditBox() {
2831
control={control}
2932
defaultValue={[]}
3033
name="forgotPasswordMethods"
31-
render={({ field: { value, onChange } }) => {
34+
rules={{
35+
validate: (value) => {
36+
const requiredConnectors = getForgotPasswordMethodsRequiredConnectors(value ?? []);
37+
return requiredConnectors.every((connectorType) =>
38+
isConnectorTypeEnabled(connectorType)
39+
);
40+
},
41+
}}
42+
render={({ field: { value, onChange }, fieldState: { error } }) => {
3243
const methods = value ?? [];
44+
3345
const availableMethods = Object.values(ForgotPasswordMethod).filter(
3446
(method) => !methods.includes(method)
3547
);
@@ -68,22 +80,31 @@ function ForgotPasswordMethodEditBox() {
6880
return (
6981
<div>
7082
<DragDropProvider>
71-
{methods.map((method: ForgotPasswordMethod, index: number) => (
72-
<DraggableItem
73-
key={method}
74-
id={method}
75-
sortIndex={index}
76-
moveItem={handleSwapMethods}
77-
className={styles.draggleItemContainer}
78-
>
79-
<VerificationMethodItem
80-
method={method}
81-
onRemove={() => {
82-
handleRemoveMethod(method);
83-
}}
84-
/>
85-
</DraggableItem>
86-
))}
83+
{(value ?? []).map((method: ForgotPasswordMethod, index: number) => {
84+
const requiredConnectors = getForgotPasswordMethodsRequiredConnectors([method]);
85+
const hasConnectorError = requiredConnectors.some(
86+
(connectorType) => !isConnectorTypeEnabled(connectorType)
87+
);
88+
89+
return (
90+
<DraggableItem
91+
key={method}
92+
id={method}
93+
sortIndex={index}
94+
moveItem={handleSwapMethods}
95+
className={styles.draggleItemContainer}
96+
>
97+
<VerificationMethodItem
98+
method={method}
99+
requiredConnectors={requiredConnectors}
100+
hasError={hasConnectorError}
101+
onRemove={() => {
102+
handleRemoveMethod(method);
103+
}}
104+
/>
105+
</DraggableItem>
106+
);
107+
})}
87108
</DragDropProvider>
88109
{availableMethods.length > 0 && (
89110
<ActionMenu

packages/console/src/pages/SignInExperience/PageContent/SignUpAndSignIn/utils.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
AlternativeSignUpIdentifier,
33
ConnectorType,
4+
ForgotPasswordMethod,
45
SignInIdentifier,
56
MfaFactor,
67
type SignUpIdentifier as SignUpIdentifierMethod,
@@ -50,3 +51,24 @@ export const getSignUpIdentifiersRequiredConnectors = (
5051

5152
return Array.from(requiredConnectors);
5253
};
54+
55+
export const getForgotPasswordMethodsRequiredConnectors = (
56+
forgotPasswordMethods: ForgotPasswordMethod[]
57+
): ConnectorType[] => {
58+
const requiredConnectors = new Set<ConnectorType>();
59+
60+
for (const method of forgotPasswordMethods) {
61+
switch (method) {
62+
case ForgotPasswordMethod.EmailVerificationCode: {
63+
requiredConnectors.add(ConnectorType.Email);
64+
continue;
65+
}
66+
case ForgotPasswordMethod.PhoneVerificationCode: {
67+
requiredConnectors.add(ConnectorType.Sms);
68+
continue;
69+
}
70+
}
71+
}
72+
73+
return Array.from(requiredConnectors);
74+
};

0 commit comments

Comments
 (0)