mirror of
https://github.com/logto-io/logto.git
synced 2024-12-30 20:33:54 -05:00
refactor(console): refactor custom connector form initialization (#4110)
This commit is contained in:
parent
bbf0551273
commit
97fc519ac1
4 changed files with 71 additions and 68 deletions
|
@ -1,7 +1,6 @@
|
|||
import { ServiceConnector } from '@logto/connector-kit';
|
||||
import { ConnectorType } from '@logto/schemas';
|
||||
import type { ConnectorResponse } from '@logto/schemas';
|
||||
import type { Optional } from '@silverhand/essentials';
|
||||
import { conditional } from '@silverhand/essentials';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { FormProvider, useForm } from 'react-hook-form';
|
||||
|
@ -19,7 +18,7 @@ import { useConnectorFormConfigParser } from '@/hooks/use-connector-form-config-
|
|||
import useDocumentationUrl from '@/hooks/use-documentation-url';
|
||||
import { SyncProfileMode } from '@/types/connector';
|
||||
import type { ConnectorFormType } from '@/types/connector';
|
||||
import { initFormData } from '@/utils/connector-form';
|
||||
import { convertResponseToForm } from '@/utils/connector-form';
|
||||
import { trySubmitSafe } from '@/utils/form';
|
||||
|
||||
import EmailServiceConnectorForm from './EmailServiceConnectorForm';
|
||||
|
@ -30,29 +29,22 @@ type Props = {
|
|||
onConnectorUpdated: (connector: ConnectorResponse) => void;
|
||||
};
|
||||
|
||||
const getConnectorTarget = (connectorData: ConnectorResponse): Optional<string> => {
|
||||
return conditional(
|
||||
connectorData.type === ConnectorType.Social &&
|
||||
!connectorData.isStandard &&
|
||||
(connectorData.metadata.target ?? connectorData.target)
|
||||
);
|
||||
};
|
||||
|
||||
function ConnectorContent({ isDeleted, connectorData, onConnectorUpdated }: Props) {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const { getDocumentationUrl } = useDocumentationUrl();
|
||||
const api = useApi();
|
||||
|
||||
const formConfig = useMemo(() => {
|
||||
const { formItems, config } = connectorData;
|
||||
return conditional(formItems && initFormData(formItems, config)) ?? {};
|
||||
}, [connectorData]);
|
||||
const formData = useMemo(() => convertResponseToForm(connectorData), [connectorData]);
|
||||
|
||||
const methods = useForm<ConnectorFormType>({
|
||||
reValidateMode: 'onBlur',
|
||||
defaultValues: {
|
||||
syncProfile: SyncProfileMode.OnlyAtRegister,
|
||||
target: getConnectorTarget(connectorData),
|
||||
...formData,
|
||||
/**
|
||||
* Note:
|
||||
* The `formConfig` will be setup in the `useEffect` hook since react-hook-form's `useForm` hook infers `Record<string, unknown>` to `{ [x: string]: {} | undefined }` incorrectly,
|
||||
* this causes we cannot apply the default value of `formConfig` to the form.
|
||||
*/
|
||||
formConfig: {},
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -61,7 +53,6 @@ function ConnectorContent({ isDeleted, connectorData, onConnectorUpdated }: Prop
|
|||
handleSubmit,
|
||||
watch,
|
||||
reset,
|
||||
setValue,
|
||||
} = methods;
|
||||
|
||||
const {
|
||||
|
@ -72,27 +63,13 @@ function ConnectorContent({ isDeleted, connectorData, onConnectorUpdated }: Prop
|
|||
isStandard: isStandardConnector,
|
||||
metadata: { logoDark },
|
||||
} = connectorData;
|
||||
|
||||
const isSocialConnector = connectorType === ConnectorType.Social;
|
||||
const isEmailServiceConnector = connectorId === ServiceConnector.Email;
|
||||
useEffect(() => {
|
||||
const { metadata, config, syncProfile } = connectorData;
|
||||
const { name, logo, logoDark } = metadata;
|
||||
|
||||
reset({
|
||||
target: getConnectorTarget(connectorData),
|
||||
logo,
|
||||
logoDark: logoDark ?? '',
|
||||
name: name?.en,
|
||||
jsonConfig: JSON.stringify(config, null, 2),
|
||||
syncProfile: syncProfile ? SyncProfileMode.EachSignIn : SyncProfileMode.OnlyAtRegister,
|
||||
});
|
||||
/**
|
||||
* Note:
|
||||
* Set `formConfig` independently.
|
||||
* Since react-hook-form's reset function infers `Record<string, unknown>` to `{ [x: string]: {} | undefined }` incorrectly.
|
||||
*/
|
||||
setValue('formConfig', formConfig, { shouldDirty: false });
|
||||
}, [connectorData, formConfig, reset, setValue]);
|
||||
useEffect(() => {
|
||||
reset(formData);
|
||||
}, [formData, reset]);
|
||||
|
||||
const configParser = useConnectorFormConfigParser();
|
||||
|
||||
|
@ -133,12 +110,6 @@ function ConnectorContent({ isDeleted, connectorData, onConnectorUpdated }: Prop
|
|||
isSubmitting={isSubmitting}
|
||||
onDiscard={() => {
|
||||
reset();
|
||||
/**
|
||||
* Note:
|
||||
* Reset `formConfig` manually since react-hook-form's `useForm` hook infers `Record<string, unknown>` to `{ [x: string]: {} | undefined }` incorrectly,
|
||||
* this causes we cannot apply the default value of `formConfig` to the form.
|
||||
*/
|
||||
setValue('formConfig', formConfig, { shouldDirty: false });
|
||||
}}
|
||||
onSubmit={onSubmit}
|
||||
>
|
||||
|
|
|
@ -29,7 +29,7 @@ import { useConnectorFormConfigParser } from '@/hooks/use-connector-form-config-
|
|||
import * as modalStyles from '@/scss/modal.module.scss';
|
||||
import type { ConnectorFormType } from '@/types/connector';
|
||||
import { SyncProfileMode } from '@/types/connector';
|
||||
import { initFormData } from '@/utils/connector-form';
|
||||
import { convertFactoryResponseToForm } from '@/utils/connector-form';
|
||||
import { trySubmitSafe } from '@/utils/form';
|
||||
|
||||
import { splitMarkdownByTitle } from '../utils';
|
||||
|
@ -50,37 +50,35 @@ function Guide({ connector, onClose }: Props) {
|
|||
const { updateConfigs } = useConfigs();
|
||||
const [conflictConnectorName, setConflictConnectorName] = useState<Record<string, string>>();
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const { type: connectorType, formItems, target, isStandard, configTemplate } = connector ?? {};
|
||||
const { type: connectorType, formItems, isStandard } = connector ?? {};
|
||||
const { language } = i18next;
|
||||
|
||||
const isSocialConnector =
|
||||
connectorType !== ConnectorType.Sms && connectorType !== ConnectorType.Email;
|
||||
|
||||
const methods = useForm<ConnectorFormType>({
|
||||
reValidateMode: 'onBlur',
|
||||
defaultValues: {
|
||||
jsonConfig: '{}',
|
||||
formConfig: {},
|
||||
syncProfile: SyncProfileMode.OnlyAtRegister,
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
formState: { isSubmitting },
|
||||
handleSubmit,
|
||||
watch,
|
||||
setError,
|
||||
reset,
|
||||
setValue,
|
||||
} = methods;
|
||||
|
||||
useEffect(() => {
|
||||
const formConfig = conditional(formItems && initFormData(formItems));
|
||||
reset({
|
||||
...(configTemplate ? { jsonConfig: configTemplate } : {}),
|
||||
...(isSocialConnector && !isStandard && target ? { target } : {}),
|
||||
syncProfile: SyncProfileMode.OnlyAtRegister,
|
||||
});
|
||||
/**
|
||||
* Note:
|
||||
* Set `formConfig` independently.
|
||||
* Since react-hook-form's reset function infers `Record<string, unknown>` to `{ [x: string]: {} | undefined }` incorrectly.
|
||||
*/
|
||||
setValue('formConfig', formConfig ?? {}, { shouldDirty: false });
|
||||
}, [formItems, reset, configTemplate, target, isSocialConnector, isStandard, setValue]);
|
||||
if (!connector) {
|
||||
return;
|
||||
}
|
||||
reset(convertFactoryResponseToForm(connector));
|
||||
}, [reset, connector]);
|
||||
|
||||
const configParser = useConnectorFormConfigParser();
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import type { ConnectorResponse } from '@logto/schemas';
|
||||
import { type Nullable } from '@silverhand/essentials';
|
||||
|
||||
export type ConnectorGroup<T = ConnectorResponse> = Pick<
|
||||
ConnectorResponse,
|
||||
|
@ -14,11 +15,11 @@ export enum SyncProfileMode {
|
|||
}
|
||||
|
||||
export type ConnectorFormType = {
|
||||
name?: string;
|
||||
logo?: string;
|
||||
logoDark?: Nullable<string>;
|
||||
target?: string;
|
||||
syncProfile: SyncProfileMode;
|
||||
jsonConfig: string; // Support editing configs by the code editor
|
||||
formConfig: Record<string, unknown>; // Support custom connector config form
|
||||
name: string;
|
||||
logo: string;
|
||||
logoDark: string;
|
||||
target: string;
|
||||
syncProfile: SyncProfileMode;
|
||||
};
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import type { ConnectorConfigFormItem } from '@logto/connector-kit';
|
||||
import { ConnectorConfigFormItemType } from '@logto/connector-kit';
|
||||
import { ConnectorConfigFormItemType, ConnectorType } from '@logto/connector-kit';
|
||||
import { type ConnectorFactoryResponse, type ConnectorResponse } from '@logto/schemas';
|
||||
import { conditional } from '@silverhand/essentials';
|
||||
|
||||
import { SyncProfileMode, type ConnectorFormType } from '@/types/connector';
|
||||
import { safeParseJson } from '@/utils/json';
|
||||
|
||||
export const initFormData = (
|
||||
formItems: ConnectorConfigFormItem[],
|
||||
config?: Record<string, unknown>
|
||||
) => {
|
||||
const initFormData = (formItems: ConnectorConfigFormItem[], config?: Record<string, unknown>) => {
|
||||
const data: Array<[string, unknown]> = formItems.map((item) => {
|
||||
const value = config?.[item.key] ?? item.defaultValue;
|
||||
|
||||
|
@ -60,3 +60,36 @@ export const parseFormConfig = (
|
|||
.filter((item): item is [string, unknown] => Array.isArray(item))
|
||||
);
|
||||
};
|
||||
|
||||
export const convertResponseToForm = (connector: ConnectorResponse): ConnectorFormType => {
|
||||
const { metadata, type, config, syncProfile, isStandard, formItems, target } = connector;
|
||||
const { name, logo, logoDark } = metadata;
|
||||
const formConfig = conditional(formItems && initFormData(formItems, config)) ?? {};
|
||||
|
||||
return {
|
||||
name: name?.en,
|
||||
logo,
|
||||
logoDark,
|
||||
target: conditional(
|
||||
type === ConnectorType.Social && !isStandard && (metadata.target ?? target)
|
||||
),
|
||||
syncProfile: syncProfile ? SyncProfileMode.EachSignIn : SyncProfileMode.OnlyAtRegister,
|
||||
jsonConfig: JSON.stringify(config, null, 2),
|
||||
formConfig,
|
||||
};
|
||||
};
|
||||
|
||||
export const convertFactoryResponseToForm = (
|
||||
connectorFactory: ConnectorFactoryResponse
|
||||
): ConnectorFormType => {
|
||||
const { formItems, configTemplate, type, isStandard, target } = connectorFactory;
|
||||
const jsonConfig = configTemplate ?? '{}';
|
||||
const formConfig = conditional(formItems && initFormData(formItems)) ?? {};
|
||||
|
||||
return {
|
||||
target: conditional(type === ConnectorType.Social && !isStandard && target),
|
||||
syncProfile: SyncProfileMode.OnlyAtRegister,
|
||||
jsonConfig,
|
||||
formConfig,
|
||||
};
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue