mirror of
https://github.com/logto-io/logto.git
synced 2025-03-24 22:41:28 -05:00
refactor(console): refactor connector creation modal (#4183)
This commit is contained in:
parent
3c51ecc29d
commit
1b0f9be88b
23 changed files with 257 additions and 145 deletions
|
@ -0,0 +1,30 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.connector {
|
||||
font: var(--font-body-2);
|
||||
display: flex;
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
margin-left: _.unit(3);
|
||||
|
||||
.name {
|
||||
font: var(--font-label-2);
|
||||
@include _.multi-line-ellipsis(1);
|
||||
padding-right: _.unit(3);
|
||||
}
|
||||
|
||||
.connectorId {
|
||||
margin-top: _.unit(1);
|
||||
font: var(--font-body-3);
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.description {
|
||||
font: var(--font-body-3);
|
||||
color: var(--color-text-secondary);
|
||||
margin-top: _.unit(1);
|
||||
@include _.multi-line-ellipsis(4);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
import { type ConnectorFactoryResponse } from '@logto/schemas';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import ConnectorLogo from '@/components/ConnectorLogo';
|
||||
import UnnamedTrans from '@/components/UnnamedTrans';
|
||||
import { type ConnectorGroup } from '@/types/connector';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
type Props = {
|
||||
data: ConnectorGroup<ConnectorFactoryResponse>;
|
||||
};
|
||||
|
||||
function ConnectorRadio({ data: { name, logo, logoDark, description } }: Props) {
|
||||
return (
|
||||
<div className={styles.connector}>
|
||||
<ConnectorLogo data={{ logo, logoDark }} />
|
||||
<div className={styles.content}>
|
||||
<div className={classNames(styles.name)}>
|
||||
<UnnamedTrans resource={name} />
|
||||
</div>
|
||||
<div className={styles.description}>
|
||||
<UnnamedTrans resource={description} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ConnectorRadio;
|
|
@ -0,0 +1,40 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
@use '@/scss/dimensions' as dim;
|
||||
|
||||
.connectorGroup {
|
||||
gap: _.unit(4);
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
|
||||
@media screen and (max-width: dim.$modal-layout-grid-large) {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
@media screen and (max-width: dim.$modal-layout-grid-medium) {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
@media screen and (max-width: dim.$modal-layout-grid-small) {
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
}
|
||||
|
||||
&.medium {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
|
||||
@media screen and (max-width: dim.$modal-layout-grid-small) {
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
&.large {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
|
||||
@media screen and (max-width: dim.$modal-layout-grid-medium) {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
@media screen and (max-width: dim.$modal-layout-grid-small) {
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
import { type ConnectorFactoryResponse } from '@logto/schemas';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import RadioGroup, { Radio } from '@/ds-components/RadioGroup';
|
||||
import { type ConnectorGroup } from '@/types/connector';
|
||||
|
||||
import ConnectorRadio from './ConnectorRadio';
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
export type ConnectorRadioGroupSize = 'medium' | 'large' | 'xlarge';
|
||||
|
||||
type Props = {
|
||||
name: string;
|
||||
value?: string;
|
||||
groups: Array<ConnectorGroup<ConnectorFactoryResponse>>;
|
||||
size: ConnectorRadioGroupSize;
|
||||
onChange: (groupId: string) => void;
|
||||
};
|
||||
|
||||
function ConnectorRadioGroup({ name, groups, value, size, onChange }: Props) {
|
||||
return (
|
||||
<RadioGroup
|
||||
name={name}
|
||||
value={value}
|
||||
type="card"
|
||||
className={classNames(styles.connectorGroup, styles[size])}
|
||||
onChange={onChange}
|
||||
>
|
||||
{groups.map((data) => (
|
||||
<Radio key={data.id} value={data.id}>
|
||||
<ConnectorRadio data={data} />
|
||||
</Radio>
|
||||
))}
|
||||
</RadioGroup>
|
||||
);
|
||||
}
|
||||
|
||||
export default ConnectorRadioGroup;
|
|
@ -1,17 +1,18 @@
|
|||
import classNames from 'classnames';
|
||||
|
||||
import * as layout from '../index.module.scss';
|
||||
import * as radioStyles from '../ConnectorRadioGroup/ConnectorRadio/index.module.scss';
|
||||
import * as radioGroupStyles from '../ConnectorRadioGroup/index.module.scss';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
function Skeleton() {
|
||||
return (
|
||||
<div className={layout.connectorGroup}>
|
||||
<div className={radioGroupStyles.connectorGroup}>
|
||||
{Array.from({ length: 8 }).map((_, index) => (
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
<div key={index} className={classNames(layout.connector, styles.connector)}>
|
||||
<div key={index} className={classNames(radioStyles.connector, styles.connector)}>
|
||||
<div className={styles.logo} />
|
||||
<div className={layout.content}>
|
||||
<div className={radioStyles.content}>
|
||||
<div className={styles.name} />
|
||||
<div>
|
||||
<div className={styles.description} />
|
||||
|
|
|
@ -1,71 +1,6 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
@use '@/scss/dimensions' as dim;
|
||||
|
||||
.body {
|
||||
.connectorGroup {
|
||||
gap: _.unit(4);
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
|
||||
@media screen and (max-width: dim.$modal-layout-grid-large) {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
@media screen and (max-width: dim.$modal-layout-grid-medium) {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
@media screen and (max-width: dim.$modal-layout-grid-small) {
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
}
|
||||
|
||||
&.medium {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
|
||||
@media screen and (max-width: dim.$modal-layout-grid-small) {
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
&.large {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
|
||||
@media screen and (max-width: dim.$modal-layout-grid-medium) {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
@media screen and (max-width: dim.$modal-layout-grid-small) {
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.connector {
|
||||
font: var(--font-body-2);
|
||||
display: flex;
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
margin-left: _.unit(3);
|
||||
|
||||
.name {
|
||||
font: var(--font-label-2);
|
||||
@include _.multi-line-ellipsis(1);
|
||||
padding-right: _.unit(3);
|
||||
}
|
||||
|
||||
.connectorId {
|
||||
margin-top: _.unit(1);
|
||||
font: var(--font-body-3);
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.description {
|
||||
font: var(--font-body-3);
|
||||
color: var(--color-text-secondary);
|
||||
margin-top: _.unit(1);
|
||||
@include _.multi-line-ellipsis(4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.standardLabel {
|
||||
font: var(--font-label-2);
|
||||
margin: _.unit(6) 0 _.unit(4);
|
||||
}
|
||||
|
|
|
@ -1,24 +1,25 @@
|
|||
import { ConnectorType } from '@logto/schemas';
|
||||
import type { ConnectorFactoryResponse, ConnectorResponse } from '@logto/schemas';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
ConnectorType,
|
||||
type ConnectorFactoryResponse,
|
||||
type ConnectorResponse,
|
||||
} from '@logto/schemas';
|
||||
import { useMemo, useState } from 'react';
|
||||
import Modal from 'react-modal';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import ConnectorLogo from '@/components/ConnectorLogo';
|
||||
import UnnamedTrans from '@/components/UnnamedTrans';
|
||||
import Button from '@/ds-components/Button';
|
||||
import DynamicT from '@/ds-components/DynamicT';
|
||||
import ModalLayout from '@/ds-components/ModalLayout';
|
||||
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 '../../pages/Connectors/utils';
|
||||
|
||||
import ConnectorRadioGroup from './ConnectorRadioGroup';
|
||||
import PlatformSelector from './PlatformSelector';
|
||||
import Skeleton from './Skeleton';
|
||||
import * as styles from './index.module.scss';
|
||||
import { getConnectorOrder } from './utils';
|
||||
import { compareConnectors, getConnectorRadioGroupSize, getModalTitle } from './utils';
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
|
@ -60,12 +61,7 @@ function CreateConnectorForm({ onClose, isOpen: isFormOpen, type }: Props) {
|
|||
}))
|
||||
.filter(({ connectors }) => !connectors.every(({ added }) => added))
|
||||
.slice()
|
||||
.sort((connectorA, connectorB) => {
|
||||
const orderA = getConnectorOrder(connectorA.target, connectorA.isStandard);
|
||||
const orderB = getConnectorOrder(connectorB.target, connectorB.isStandard);
|
||||
|
||||
return orderA - orderB;
|
||||
});
|
||||
.sort(compareConnectors);
|
||||
}, [factories, type, existingConnectors]);
|
||||
|
||||
const activeGroup = useMemo(
|
||||
|
@ -73,41 +69,11 @@ function CreateConnectorForm({ onClose, isOpen: isFormOpen, type }: Props) {
|
|||
[activeGroupId, groups]
|
||||
);
|
||||
|
||||
const cardTitle = useMemo(() => {
|
||||
if (type === ConnectorType.Email) {
|
||||
return 'connectors.setup_title.email';
|
||||
}
|
||||
|
||||
if (type === ConnectorType.Sms) {
|
||||
return 'connectors.setup_title.sms';
|
||||
}
|
||||
|
||||
return 'connectors.setup_title.social';
|
||||
}, [type]);
|
||||
|
||||
const modalSize = useMemo(() => {
|
||||
/**
|
||||
* Note:
|
||||
* Fix the size to large, since now we have little passwordless connectors.
|
||||
*/
|
||||
if (type !== ConnectorType.Social) {
|
||||
return 'large';
|
||||
}
|
||||
|
||||
if (groups.length <= 2) {
|
||||
return 'medium';
|
||||
}
|
||||
|
||||
if (groups.length === 3) {
|
||||
return 'large';
|
||||
}
|
||||
|
||||
return 'xlarge';
|
||||
}, [groups.length, type]);
|
||||
|
||||
if (!isFormOpen) {
|
||||
return null;
|
||||
}
|
||||
const cardTitle = useMemo(() => getModalTitle(type), [type]);
|
||||
const radioGroupSize = useMemo(
|
||||
() => getConnectorRadioGroupSize(groups.length, type),
|
||||
[groups.length, type]
|
||||
);
|
||||
|
||||
const handleGroupChange = (groupId: string) => {
|
||||
setActiveGroupId(groupId);
|
||||
|
@ -123,6 +89,17 @@ function CreateConnectorForm({ onClose, isOpen: isFormOpen, type }: Props) {
|
|||
setActiveFactoryId(firstAvailableConnector?.id);
|
||||
};
|
||||
|
||||
const defaultGroups = useMemo(
|
||||
() => (type === ConnectorType.Social ? groups.filter((group) => !group.isStandard) : groups),
|
||||
[groups, type]
|
||||
);
|
||||
|
||||
const standardGroups = useMemo(() => groups.filter((group) => group.isStandard), [groups]);
|
||||
|
||||
if (!isFormOpen) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
shouldCloseOnEsc
|
||||
|
@ -145,35 +122,32 @@ function CreateConnectorForm({ onClose, isOpen: isFormOpen, type }: Props) {
|
|||
}}
|
||||
/>
|
||||
}
|
||||
className={styles.body}
|
||||
size={modalSize}
|
||||
size={radioGroupSize}
|
||||
onClose={onClose}
|
||||
>
|
||||
{isLoading && <Skeleton />}
|
||||
{factoriesError?.message ?? connectorsError?.message}
|
||||
<RadioGroup
|
||||
<ConnectorRadioGroup
|
||||
name="group"
|
||||
groups={defaultGroups}
|
||||
value={activeGroupId}
|
||||
type="card"
|
||||
className={classNames(styles.connectorGroup, styles[modalSize])}
|
||||
size={radioGroupSize}
|
||||
onChange={handleGroupChange}
|
||||
>
|
||||
{groups.map(({ id, name, logo, logoDark, description }) => (
|
||||
<Radio key={id} value={id}>
|
||||
<div className={styles.connector}>
|
||||
<ConnectorLogo data={{ logo, logoDark }} />
|
||||
<div className={styles.content}>
|
||||
<div className={classNames(styles.name)}>
|
||||
<UnnamedTrans resource={name} />
|
||||
</div>
|
||||
<div className={styles.description}>
|
||||
<UnnamedTrans resource={description} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Radio>
|
||||
))}
|
||||
</RadioGroup>
|
||||
/>
|
||||
{standardGroups.length > 0 && (
|
||||
<>
|
||||
<div className={styles.standardLabel}>
|
||||
<DynamicT forKey="connectors.standard_connectors" />
|
||||
</div>
|
||||
<ConnectorRadioGroup
|
||||
name="group"
|
||||
groups={standardGroups}
|
||||
value={activeGroupId}
|
||||
size={radioGroupSize}
|
||||
onChange={handleGroupChange}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{activeGroup && (
|
||||
<PlatformSelector
|
||||
connectorGroup={activeGroup}
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import { type AdminConsoleKey } from '@logto/phrases';
|
||||
import { ConnectorType, type ConnectorResponse } from '@logto/schemas';
|
||||
|
||||
import { type ConnectorRadioGroupSize } from './ConnectorRadioGroup';
|
||||
import { featuredConnectorTargets } from './constants';
|
||||
|
||||
export const getConnectorOrder = (target: string, isStandard?: boolean): number => {
|
||||
const getConnectorOrder = (target: string, isStandard?: boolean): number => {
|
||||
const order = featuredConnectorTargets.indexOf(target);
|
||||
|
||||
if (order === -1) {
|
||||
|
@ -10,3 +14,48 @@ export const getConnectorOrder = (target: string, isStandard?: boolean): number
|
|||
|
||||
return order;
|
||||
};
|
||||
|
||||
export const compareConnectors = <T extends Pick<ConnectorResponse, 'target' | 'isStandard'>>(
|
||||
connectorA: T,
|
||||
connectorB: T
|
||||
) => {
|
||||
const orderA = getConnectorOrder(connectorA.target, connectorA.isStandard);
|
||||
const orderB = getConnectorOrder(connectorB.target, connectorB.isStandard);
|
||||
|
||||
return orderA - orderB;
|
||||
};
|
||||
|
||||
export const getConnectorRadioGroupSize = (
|
||||
connectorCount: number,
|
||||
connectorType?: ConnectorType
|
||||
): ConnectorRadioGroupSize => {
|
||||
/**
|
||||
* Note:
|
||||
* Fix the size to large, since now we have little passwordless connectors.
|
||||
*/
|
||||
if (connectorType !== ConnectorType.Social) {
|
||||
return 'large';
|
||||
}
|
||||
|
||||
if (connectorCount <= 2) {
|
||||
return 'medium';
|
||||
}
|
||||
|
||||
if (connectorCount === 3) {
|
||||
return 'large';
|
||||
}
|
||||
|
||||
return 'xlarge';
|
||||
};
|
||||
|
||||
export const getModalTitle = (connectorType?: ConnectorType): AdminConsoleKey => {
|
||||
if (connectorType === ConnectorType.Email) {
|
||||
return 'connectors.setup_title.email';
|
||||
}
|
||||
|
||||
if (connectorType === ConnectorType.Sms) {
|
||||
return 'connectors.setup_title.sms';
|
||||
}
|
||||
|
||||
return 'connectors.setup_title.social';
|
||||
};
|
||||
|
|
|
@ -88,6 +88,7 @@ const connectors = {
|
|||
drawer_title: 'Connector Anleitung',
|
||||
drawer_subtitle: 'Folge den Anweisungen, um deinen Connector zu integrieren',
|
||||
unknown: 'Unbekannter Connector',
|
||||
standard_connectors: 'Standard-Connectoren',
|
||||
};
|
||||
|
||||
export default connectors;
|
||||
|
|
|
@ -87,6 +87,7 @@ const connectors = {
|
|||
drawer_title: 'Connector Guide',
|
||||
drawer_subtitle: 'Follow the instructions to integrate your connector',
|
||||
unknown: 'Unknown Connector',
|
||||
standard_connectors: 'Standard connectors',
|
||||
};
|
||||
|
||||
export default connectors;
|
||||
|
|
|
@ -88,6 +88,7 @@ const connectors = {
|
|||
drawer_title: 'Guía del conector',
|
||||
drawer_subtitle: 'Siga las instrucciones para integrar su conector',
|
||||
unknown: 'Conector desconocido',
|
||||
standard_connectors: 'Conectores estándar',
|
||||
};
|
||||
|
||||
export default connectors;
|
||||
|
|
|
@ -90,6 +90,7 @@ const connectors = {
|
|||
drawer_title: 'Guide des connecteurs',
|
||||
drawer_subtitle: 'Suivez les instructions pour intégrer votre connecteur',
|
||||
unknown: 'Connecteur inconnu',
|
||||
standard_connectors: 'Connecteurs standard',
|
||||
};
|
||||
|
||||
export default connectors;
|
||||
|
|
|
@ -88,6 +88,7 @@ const connectors = {
|
|||
drawer_title: 'Guida per il connettore',
|
||||
drawer_subtitle: 'Segui le istruzioni per integrare il tuo connettore',
|
||||
unknown: 'Connettore sconosciuto',
|
||||
standard_connectors: 'Connettori standard',
|
||||
};
|
||||
|
||||
export default connectors;
|
||||
|
|
|
@ -86,6 +86,7 @@ const connectors = {
|
|||
drawer_title: 'コネクターガイド',
|
||||
drawer_subtitle: 'インテグレーションの手順に従ってください',
|
||||
unknown: '不明なコネクタ',
|
||||
standard_connectors: '標準コネクタ',
|
||||
};
|
||||
|
||||
export default connectors;
|
||||
|
|
|
@ -83,6 +83,7 @@ const connectors = {
|
|||
drawer_title: '연동 가이드',
|
||||
drawer_subtitle: '연동하기 위해 가이드를 따라 주세요.',
|
||||
unknown: '알 수 없는 연동',
|
||||
standard_connectors: '기본 커넥터',
|
||||
};
|
||||
|
||||
export default connectors;
|
||||
|
|
|
@ -87,6 +87,7 @@ const connectors = {
|
|||
drawer_title: 'Poradnik łącznika',
|
||||
drawer_subtitle: 'Postępuj zgodnie z instrukcjami, aby zintegrować swój łącznik',
|
||||
unknown: 'Nieznany Łącznik',
|
||||
standard_connectors: 'Standardowe łączniki',
|
||||
};
|
||||
|
||||
export default connectors;
|
||||
|
|
|
@ -22,7 +22,7 @@ const connectors = {
|
|||
'Fora de uso significa que sua experiência de login não usou esse método de login. <a>{{link}}</a> para adicionar este método de login. ',
|
||||
go_to_sie: 'Vá para a experiência de login',
|
||||
},
|
||||
placeholder_title: 'Social connector',
|
||||
placeholder_title: 'Conector social',
|
||||
placeholder_description:
|
||||
'Logto tem fornecido muitos conectores de login social amplamente utilizados enquanto você pode criar o seu próprio com padrões padrão.',
|
||||
save_and_done: 'Salvar e completar',
|
||||
|
@ -43,7 +43,7 @@ const connectors = {
|
|||
test_connection: 'Teste de conexão',
|
||||
name: 'Nome do botão de login social',
|
||||
name_placeholder: 'Insira o nome do botão de login social',
|
||||
name_tip: 'O nome do botão do conector será exibido como "Continue com {{Nome do Conector}}".',
|
||||
name_tip: 'O nome do botão do conector será exibido como "Continuar com {{Nome do Conector}}".',
|
||||
logo: 'URL do logo para o botão de login social',
|
||||
logo_placeholder: 'https://your.cdn.domain/logo.png',
|
||||
logo_tip: 'A imagem do logotipo também será exibida no botão do conector.',
|
||||
|
@ -86,6 +86,7 @@ const connectors = {
|
|||
drawer_title: 'Guia do Conector',
|
||||
drawer_subtitle: 'Siga as instruções para integrar seu conector',
|
||||
unknown: 'Conector desconhecido',
|
||||
standard_connectors: 'Conectores padrão',
|
||||
};
|
||||
|
||||
export default connectors;
|
||||
|
|
|
@ -88,6 +88,7 @@ const connectors = {
|
|||
drawer_title: 'Guia do conector',
|
||||
drawer_subtitle: 'Siga as instruções para integrar o conector',
|
||||
unknown: 'Conector desconhecido',
|
||||
standard_connectors: 'Conectores padrão',
|
||||
};
|
||||
|
||||
export default connectors;
|
||||
|
|
|
@ -87,6 +87,7 @@ const connectors = {
|
|||
drawer_title: 'Руководство Коннектора',
|
||||
drawer_subtitle: 'Следуйте инструкциям, чтобы интегрировать свой коннектор',
|
||||
unknown: 'Неизвестный коннектор',
|
||||
standard_connectors: 'стандартные разъемы',
|
||||
};
|
||||
|
||||
export default connectors;
|
||||
|
|
|
@ -87,6 +87,7 @@ const connectors = {
|
|||
drawer_title: 'Connector Kılavuzu',
|
||||
drawer_subtitle: 'Connectorı entegre etmek için yönergeleri izleyin',
|
||||
unknown: 'Bilinmeyen connector',
|
||||
standard_connectors: 'Standart connectorlar',
|
||||
};
|
||||
|
||||
export default connectors;
|
||||
|
|
|
@ -78,6 +78,7 @@ const connectors = {
|
|||
drawer_title: '连接器配置指南',
|
||||
drawer_subtitle: '参考以下步骤完善或修改你的连接器设置',
|
||||
unknown: '未知连接器',
|
||||
standard_connectors: '标准连接器',
|
||||
};
|
||||
|
||||
export default connectors;
|
||||
|
|
|
@ -78,6 +78,7 @@ const connectors = {
|
|||
drawer_title: '連接器配置指南',
|
||||
drawer_subtitle: '參考以下步驟完善或修改你的連接器設置',
|
||||
unknown: '未知連接器',
|
||||
standard_connectors: '標準連接器',
|
||||
};
|
||||
|
||||
export default connectors;
|
||||
|
|
|
@ -78,6 +78,7 @@ const connectors = {
|
|||
drawer_title: '連接器配置指南',
|
||||
drawer_subtitle: '參考以下步驟完善或修改你的連接器設置',
|
||||
unknown: '未知連接器',
|
||||
standard_connectors: '標準連接器',
|
||||
};
|
||||
|
||||
export default connectors;
|
||||
|
|
Loading…
Add table
Reference in a new issue