0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-30 20:33:54 -05:00

feat(console): create email service connector (#4108)

This commit is contained in:
Xiao Yijun 2023-07-05 12:09:08 +08:00 committed by GitHub
parent 44c09baba9
commit 244537ca03
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 69 additions and 24 deletions

View file

@ -13,7 +13,7 @@ import RadioGroup, { Radio } from '@/ds-components/RadioGroup';
import type { RequestError } from '@/hooks/use-api';
import * as modalStyles from '@/scss/modal.module.scss';
import { getConnectorGroups } from '../utils';
import { getConnectorGroups } from '../../pages/Connectors/utils';
import PlatformSelector from './PlatformSelector';
import Skeleton from './Skeleton';
@ -26,7 +26,7 @@ type Props = {
onClose?: (connectorId?: string) => void;
};
function CreateForm({ onClose, isOpen: isFormOpen, type }: Props) {
function CreateConnectorForm({ onClose, isOpen: isFormOpen, type }: Props) {
const { data: existingConnectors, error: connectorsError } = useSWR<
ConnectorResponse[],
RequestError
@ -186,4 +186,4 @@ function CreateForm({ onClose, isOpen: isFormOpen, type }: Props) {
);
}
export default CreateForm;
export default CreateConnectorForm;

View file

@ -18,7 +18,7 @@ export class RequestError extends Error {
}
}
type StaticApiProps = {
export type StaticApiProps = {
prefixUrl?: URL;
hideErrorToast?: boolean;
resourceIndicator?: string;

View file

@ -0,0 +1,25 @@
import { type ConnectorResponse } from '@logto/schemas';
import useApi, { type StaticApiProps } from './use-api';
import useConfigs from './use-configs';
const useConnectorApi = (props: Omit<StaticApiProps, 'prefixUrl'> = {}) => {
const api = useApi(props);
const { updateConfigs } = useConfigs();
const createConnector = async (payload: unknown) => {
const connector = await api
.post('api/connectors', {
json: payload,
})
.json<ConnectorResponse>();
await updateConfigs({ passwordlessConfigured: true });
return connector;
};
return {
createConnector,
};
};
export default useConnectorApi;

View file

@ -1,4 +1,5 @@
import { withAppInsights } from '@logto/app-insights/react';
import { ServiceConnector } from '@logto/connector-kit';
import { ConnectorType } from '@logto/schemas';
import type { ConnectorFactoryResponse, ConnectorResponse } from '@logto/schemas';
import { useEffect, useState } from 'react';
@ -11,6 +12,7 @@ import Delete from '@/assets/icons/delete.svg';
import More from '@/assets/icons/more.svg';
import Reset from '@/assets/icons/reset.svg';
import ConnectorLogo from '@/components/ConnectorLogo';
import CreateConnectorForm from '@/components/CreateConnectorForm';
import DeleteConnectorConfirmModal from '@/components/DeleteConnectorConfirmModal';
import DetailsPage from '@/components/DetailsPage';
import Drawer from '@/components/Drawer';
@ -26,10 +28,9 @@ import TabNav, { TabNavItem } from '@/ds-components/TabNav';
import Tag from '@/ds-components/Tag';
import type { RequestError } from '@/hooks/use-api';
import useApi from '@/hooks/use-api';
import useConnectorApi from '@/hooks/use-connector-api';
import useConnectorInUse from '@/hooks/use-connector-in-use';
import CreateForm from '../Connectors/CreateForm';
import ConnectorContent from './ConnectorContent';
import ConnectorTabs from './ConnectorTabs';
import ConnectorTypeName from './ConnectorTypeName';
@ -43,6 +44,7 @@ const getConnectorsPathname = (isSocial: boolean) =>
function ConnectorDetails() {
const { pathname } = useLocation();
const { connectorId } = useParams();
const { createConnector } = useConnectorApi();
const { mutate: mutateGlobal } = useSWRConfig();
const [isDeleted, setIsDeleted] = useState(false);
const [isReadMeOpen, setIsReadMeOpen] = useState(false);
@ -199,13 +201,25 @@ function ConnectorDetails() {
{t('general.delete')}
</ActionMenuItem>
</ActionMenu>
<CreateForm
<CreateConnectorForm
isOpen={isSetupOpen}
type={data.type}
onClose={(connectorId?: string) => {
onClose={async (connectorId?: string) => {
setIsSetupOpen(false);
if (connectorId) {
/**
* Note:
* The "Email Service Connector" is a built-in connector that can be directly created without the need for setup in the guide.
*/
if (connectorId === ServiceConnector.Email) {
const created = await createConnector({ connectorId });
navigate(`/connectors/${ConnectorsTabs.Passwordless}/${created.id}`, {
replace: true,
});
return;
}
navigate(`${getConnectorsPathname(isSocial)}/guide/${connectorId}`);
}
}}

View file

@ -1,6 +1,6 @@
import { isLanguageTag } from '@logto/language-kit';
import { ConnectorType } from '@logto/schemas';
import type { ConnectorFactoryResponse, RequestErrorBody, ConnectorResponse } from '@logto/schemas';
import type { ConnectorFactoryResponse, RequestErrorBody } from '@logto/schemas';
import { generateStandardId } from '@logto/shared/universal';
import { conditional } from '@silverhand/essentials';
import i18next from 'i18next';
@ -23,8 +23,7 @@ import CardTitle from '@/ds-components/CardTitle';
import DangerousRaw from '@/ds-components/DangerousRaw';
import IconButton from '@/ds-components/IconButton';
import OverlayScrollbar from '@/ds-components/OverlayScrollbar';
import useApi from '@/hooks/use-api';
import useConfigs from '@/hooks/use-configs';
import useConnectorApi from '@/hooks/use-connector-api';
import { useConnectorFormConfigParser } from '@/hooks/use-connector-form-config-parser';
import * as modalStyles from '@/scss/modal.module.scss';
import type { ConnectorFormType } from '@/types/connector';
@ -44,10 +43,9 @@ type Props = {
};
function Guide({ connector, onClose }: Props) {
const api = useApi({ hideErrorToast: true });
const { createConnector } = useConnectorApi({ hideErrorToast: true });
const navigate = useNavigate();
const callbackConnectorId = useRef(generateStandardId());
const { updateConfigs } = useConfigs();
const [conflictConnectorName, setConflictConnectorName] = useState<Record<string, string>>();
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const { type: connectorType, formItems, isStandard } = connector ?? {};
@ -126,13 +124,7 @@ function Guide({ connector, onClose }: Props) {
: basePayload;
try {
const createdConnector = await api
.post('api/connectors', {
json: payload,
})
.json<ConnectorResponse>();
await updateConfigs({ passwordlessConfigured: true });
const createdConnector = await createConnector(payload);
onClose();
toast.success(t('general.saved'));

View file

@ -1,4 +1,5 @@
import { withAppInsights } from '@logto/app-insights/react';
import { ServiceConnector } from '@logto/connector-kit';
import { ConnectorType } from '@logto/schemas';
import type { ConnectorFactoryResponse } from '@logto/schemas';
import { conditional } from '@silverhand/essentials';
@ -10,6 +11,7 @@ import useSWR from 'swr';
import Plus from '@/assets/icons/plus.svg';
import SocialConnectorEmptyDark from '@/assets/images/social-connector-empty-dark.svg';
import SocialConnectorEmpty from '@/assets/images/social-connector-empty.svg';
import CreateConnectorForm from '@/components/CreateConnectorForm';
import ListPage from '@/components/ListPage';
import { defaultEmailConnectorGroup, defaultSmsConnectorGroup } from '@/consts';
import { ConnectorsTabs } from '@/consts/page-tabs';
@ -17,6 +19,7 @@ import Button from '@/ds-components/Button';
import TabNav, { TabNavItem } from '@/ds-components/TabNav';
import TablePlaceholder from '@/ds-components/Table/TablePlaceholder';
import type { RequestError } from '@/hooks/use-api';
import useConnectorApi from '@/hooks/use-connector-api';
import useConnectorGroups from '@/hooks/use-connector-groups';
import useDocumentationUrl from '@/hooks/use-documentation-url';
import DemoConnectorNotice from '@/onboarding/components/DemoConnectorNotice';
@ -26,7 +29,6 @@ import ConnectorName from './ConnectorName';
import ConnectorStatus from './ConnectorStatus';
import ConnectorStatusField from './ConnectorStatusField';
import ConnectorTypeColumn from './ConnectorTypeColumn';
import CreateForm from './CreateForm';
import Guide from './Guide';
import SignInExperienceSetupNotice from './SignInExperienceSetupNotice';
import * as styles from './index.module.scss';
@ -63,7 +65,7 @@ function Connectors() {
const isSocial = tab === ConnectorsTabs.Social;
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const { getDocumentationUrl } = useDocumentationUrl();
const { createConnector } = useConnectorApi();
const { data, error, mutate } = useConnectorGroups();
const { data: factories, error: factoriesError } = useSWR<
ConnectorFactoryResponse[],
@ -201,11 +203,23 @@ function Connectors() {
}}
widgets={
<>
<CreateForm
<CreateConnectorForm
isOpen={Boolean(createConnectorType)}
type={createConnectorType}
onClose={(id) => {
onClose={async (id) => {
if (createConnectorType && id) {
/**
* Note:
* The "Email Service Connector" is a built-in connector that can be directly created without the need for setup in the guide.
*/
if (id === ServiceConnector.Email) {
const created = await createConnector({ connectorId: id });
navigate(`/connectors/${ConnectorsTabs.Passwordless}/${created.id}`, {
replace: true,
});
return;
}
navigate(buildGuidePathname(createConnectorType, id), { replace: true });
return;