From bbf055127391fe8ce4b4456545ad3e645e43048b Mon Sep 17 00:00:00 2001 From: Xiao Yijun Date: Mon, 3 Jul 2023 16:35:36 +0800 Subject: [PATCH] feat(console): implement email service connector details page (#4089) --- .../connector-logto-email/src/constant.ts | 15 +++ .../src/components/ConnectorTester/index.tsx | 10 +- .../index.module.scss | 5 + .../EmailServiceConnectorForm/index.tsx | 116 ++++++++++++++++++ .../ConnectorContent/index.tsx | 56 +++++---- 5 files changed, 178 insertions(+), 24 deletions(-) create mode 100644 packages/console/src/pages/ConnectorDetails/ConnectorContent/EmailServiceConnectorForm/index.module.scss create mode 100644 packages/console/src/pages/ConnectorDetails/ConnectorContent/EmailServiceConnectorForm/index.tsx diff --git a/packages/connectors/connector-logto-email/src/constant.ts b/packages/connectors/connector-logto-email/src/constant.ts index f9d8b2bd8..bd47c8eed 100644 --- a/packages/connectors/connector-logto-email/src/constant.ts +++ b/packages/connectors/connector-logto-email/src/constant.ts @@ -80,6 +80,21 @@ export const defaultMetadata: ConnectorMetadata = { type: ConnectorConfigFormItemType.Text, required: true, }, + { + key: 'fromName', + label: 'From Name', + type: ConnectorConfigFormItemType.Text, + }, + { + key: 'companyAddress', + label: 'Company Address', + type: ConnectorConfigFormItemType.Text, + }, + { + key: 'appLogo', + label: 'App Logo', + type: ConnectorConfigFormItemType.Text, + }, ], }; diff --git a/packages/console/src/components/ConnectorTester/index.tsx b/packages/console/src/components/ConnectorTester/index.tsx index 960e260b9..94b465b28 100644 --- a/packages/console/src/components/ConnectorTester/index.tsx +++ b/packages/console/src/components/ConnectorTester/index.tsx @@ -1,3 +1,4 @@ +import { ServiceConnector } from '@logto/connector-kit'; import { emailRegEx, phoneInputRegEx } from '@logto/core-kit'; import { ConnectorType } from '@logto/schemas'; import { conditional } from '@silverhand/essentials'; @@ -40,6 +41,7 @@ function ConnectorTester({ connectorFactoryId, connectorType, className, parse } const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); const api = useApi(); const isSms = connectorType === ConnectorType.Sms; + const isEmailServiceConnector = connectorFactoryId === ServiceConnector.Email; useEffect(() => { if (!showTooltip) { @@ -110,7 +112,13 @@ function ConnectorTester({ connectorFactoryId, connectorType, className, parse } /> -
{t('connector_details.test_sender_description')}
+
+ {t( + isEmailServiceConnector + ? 'connector_details.logto_email.test_notes' + : 'connector_details.test_sender_description' + )} +
{inputError?.message}
); diff --git a/packages/console/src/pages/ConnectorDetails/ConnectorContent/EmailServiceConnectorForm/index.module.scss b/packages/console/src/pages/ConnectorDetails/ConnectorContent/EmailServiceConnectorForm/index.module.scss new file mode 100644 index 000000000..d0233f195 --- /dev/null +++ b/packages/console/src/pages/ConnectorDetails/ConnectorContent/EmailServiceConnectorForm/index.module.scss @@ -0,0 +1,5 @@ +@use '@/scss/underscore' as _; + +.imageFieldHeadline { + margin-bottom: _.unit(2); +} diff --git a/packages/console/src/pages/ConnectorDetails/ConnectorContent/EmailServiceConnectorForm/index.tsx b/packages/console/src/pages/ConnectorDetails/ConnectorContent/EmailServiceConnectorForm/index.tsx new file mode 100644 index 000000000..92770953e --- /dev/null +++ b/packages/console/src/pages/ConnectorDetails/ConnectorContent/EmailServiceConnectorForm/index.tsx @@ -0,0 +1,116 @@ +import { urlRegEx } from '@logto/connector-kit'; +import { conditional, conditionalString } from '@silverhand/essentials'; +import { Controller, useFormContext } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { z } from 'zod'; + +import FormCard from '@/components/FormCard'; +import DynamicT from '@/ds-components/DynamicT'; +import FormField from '@/ds-components/FormField'; +import TextInput from '@/ds-components/TextInput'; +import ImageUploaderField from '@/ds-components/Uploader/ImageUploaderField'; +import useUserAssetsService from '@/hooks/use-user-assets-service'; +import { type ConnectorFormType } from '@/types/connector'; +import { uriValidator } from '@/utils/validator'; + +import * as styles from './index.module.scss'; + +type Props = { + extraInfo?: Record; +}; + +const extraInfoGuard = z.object({ + fromEmail: z.string(), +}); + +/** + * Note: + * This `EmailServiceConnectorForm` is hard-coded since the custom connector config form does not support i18n and we need i18n for our built-in connectors. + * + * TODO: @xiaoyijun @darcyYe remove this hard-coded form once the custom connector config form supports i18n. + */ +function EmailServiceConnectorForm({ extraInfo }: Props) { + const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); + const { isReady: isUserAssetsServiceReady } = useUserAssetsService(); + const parsedExtraInfo = extraInfoGuard.safeParse(extraInfo ?? {}); + + const { + control, + register, + formState: { + errors: { formConfig: fromConfigErrors }, + }, + } = useFormContext(); + + const validateInput = (value: string) => { + const containUrl = urlRegEx.test(value); + return containUrl ? t('connector_details.logto_email.urls_not_allowed') : true; + }; + + return ( + + + + + } + > + validateInput(conditionalString(value)), + })} + error={fromConfigErrors?.fromName?.message} + placeholder={t('connector_details.logto_email.from_name_placeholder')} + /> + + } + > + validateInput(conditionalString(value)), + })} + error={fromConfigErrors?.companyAddress?.message} + placeholder={t('connector_details.logto_email.company_address_placeholder')} + /> + + } + headlineClassName={conditional(isUserAssetsServiceReady && styles.imageFieldHeadline)} + > + {isUserAssetsServiceReady ? ( + ( + + )} + /> + ) : ( + + !value || uriValidator(conditionalString(value)) || t('errors.invalid_uri_format'), + })} + error={fromConfigErrors?.appLogo?.message} + /> + )} + + + ); +} + +export default EmailServiceConnectorForm; diff --git a/packages/console/src/pages/ConnectorDetails/ConnectorContent/index.tsx b/packages/console/src/pages/ConnectorDetails/ConnectorContent/index.tsx index 8d7d1e306..2cf3e39b6 100644 --- a/packages/console/src/pages/ConnectorDetails/ConnectorContent/index.tsx +++ b/packages/console/src/pages/ConnectorDetails/ConnectorContent/index.tsx @@ -1,3 +1,4 @@ +import { ServiceConnector } from '@logto/connector-kit'; import { ConnectorType } from '@logto/schemas'; import type { ConnectorResponse } from '@logto/schemas'; import type { Optional } from '@silverhand/essentials'; @@ -21,6 +22,8 @@ import type { ConnectorFormType } from '@/types/connector'; import { initFormData } from '@/utils/connector-form'; import { trySubmitSafe } from '@/utils/form'; +import EmailServiceConnectorForm from './EmailServiceConnectorForm'; + type Props = { isDeleted: boolean; connectorData: ConnectorResponse; @@ -60,8 +63,17 @@ function ConnectorContent({ isDeleted, connectorData, onConnectorUpdated }: Prop reset, setValue, } = methods; - const isSocialConnector = connectorData.type === ConnectorType.Social; + const { + id, + connectorId, + type: connectorType, + formItems, + 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; @@ -136,32 +148,30 @@ function ConnectorContent({ isDeleted, connectorData, onConnectorUpdated }: Prop description="connector_details.settings_description" learnMoreLink={getDocumentationUrl('/docs/references/connectors')} > - + )} - - - - {/* Tell typescript that the connectorType is Email or Sms */} - {connectorData.type !== ConnectorType.Social && ( + {isEmailServiceConnector ? ( + + ) : ( + + + + )} + {!isSocialConnector && ( configParser(watch(), connectorData.formItems)} + connectorFactoryId={connectorId} + connectorType={connectorType} + parse={() => configParser(watch(), formItems)} /> )}