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:
parent
44c09baba9
commit
244537ca03
13 changed files with 69 additions and 24 deletions
|
@ -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;
|
|
@ -18,7 +18,7 @@ export class RequestError extends Error {
|
|||
}
|
||||
}
|
||||
|
||||
type StaticApiProps = {
|
||||
export type StaticApiProps = {
|
||||
prefixUrl?: URL;
|
||||
hideErrorToast?: boolean;
|
||||
resourceIndicator?: string;
|
||||
|
|
25
packages/console/src/hooks/use-connector-api.ts
Normal file
25
packages/console/src/hooks/use-connector-api.ts
Normal 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;
|
|
@ -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}`);
|
||||
}
|
||||
}}
|
||||
|
|
|
@ -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'));
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue