mirror of
https://github.com/logto-io/logto.git
synced 2025-04-07 23:01:25 -05:00
feat(console): connector groups table (#962)
This commit is contained in:
parent
fb49b4e100
commit
eb3f0cbf5b
10 changed files with 105 additions and 38 deletions
|
@ -3,7 +3,7 @@
|
|||
.status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font: var(--font-label-medium);
|
||||
font: var(--font-body-medium);
|
||||
|
||||
.icon {
|
||||
width: 10px;
|
||||
|
@ -23,6 +23,7 @@
|
|||
padding: _.unit(0.5) _.unit(2);
|
||||
border-radius: 10px;
|
||||
background: var(--color-success-99);
|
||||
font: var(--font-label-medium);
|
||||
|
||||
&.disabled {
|
||||
background: var(--color-neutral-variant-95);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { I18nKey } from '@logto/phrases';
|
||||
import { ConnectorType } from '@logto/schemas';
|
||||
import { ConnectorPlatform, ConnectorType } from '@logto/schemas';
|
||||
|
||||
import emailConnectorIcon from '@/assets/images/connector-email.svg';
|
||||
import smsConnectorIcon from '@/assets/images/connector-sms.svg';
|
||||
|
@ -24,3 +24,13 @@ export const connectorIconPlaceHolder: IconPlaceHolder = Object.freeze({
|
|||
// Note: we don't need icon placeholder for social connector
|
||||
[ConnectorType.Social]: '',
|
||||
});
|
||||
|
||||
type ConnectorPlatformLabel = {
|
||||
[key in ConnectorPlatform]: I18nKey;
|
||||
};
|
||||
|
||||
export const connectorPlatformLabel: ConnectorPlatformLabel = Object.freeze({
|
||||
[ConnectorPlatform.Native]: 'admin_console.connectors.platform.native',
|
||||
[ConnectorPlatform.Universal]: 'admin_console.connectors.platform.universal',
|
||||
[ConnectorPlatform.Web]: 'admin_console.connectors.platform.web',
|
||||
});
|
||||
|
|
|
@ -21,6 +21,7 @@ const useConnectorGroups = () => {
|
|||
return [
|
||||
...previous,
|
||||
{
|
||||
id: item.id, // Take first connector's id as groupId, only used for indexing.
|
||||
name: item.name,
|
||||
logo: item.logo,
|
||||
target: item.target,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { ConnectorDTO, ConnectorType } from '@logto/schemas';
|
||||
import { conditional } from '@silverhand/essentials';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
@ -12,9 +13,10 @@ import * as styles from './index.module.scss';
|
|||
type Props = {
|
||||
type: ConnectorType;
|
||||
connector?: ConnectorDTO;
|
||||
isShowId?: boolean;
|
||||
};
|
||||
|
||||
const ConnectorName = ({ type, connector }: Props) => {
|
||||
const ConnectorName = ({ type, connector, isShowId = false }: Props) => {
|
||||
const { t } = useTranslation(undefined);
|
||||
|
||||
if (!connector) {
|
||||
|
@ -30,7 +32,7 @@ const ConnectorName = ({ type, connector }: Props) => {
|
|||
<Link to={`/connectors/${connector.id}`} className={styles.link}>
|
||||
<ItemPreview
|
||||
title={<UnnamedTrans resource={connector.name} />}
|
||||
subtitle={connector.id}
|
||||
subtitle={conditional(isShowId && connector.id)}
|
||||
icon={<img className={styles.logo} src={connector.logo} />}
|
||||
/>
|
||||
</Link>
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.statusItems {
|
||||
display: flex;
|
||||
|
||||
.statusItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&:not(:last-child) {
|
||||
.line {
|
||||
margin: 0 _.unit(2);
|
||||
height: 16px;
|
||||
@include _.vertical-bar;
|
||||
}
|
||||
}
|
||||
|
||||
.platform {
|
||||
margin-left: _.unit(1);
|
||||
color: var(--color-caption);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,39 +4,54 @@ import { useTranslation } from 'react-i18next';
|
|||
|
||||
import Button from '@/components/Button';
|
||||
import Status from '@/components/Status';
|
||||
import { connectorTitlePlaceHolder } from '@/consts/connectors';
|
||||
import { connectorPlatformLabel, connectorTitlePlaceHolder } from '@/consts/connectors';
|
||||
|
||||
import ConnectorName from '../ConnectorName';
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
type Props = {
|
||||
type: ConnectorType;
|
||||
connector?: ConnectorDTO;
|
||||
connectors: ConnectorDTO[];
|
||||
onClickSetup?: () => void;
|
||||
};
|
||||
|
||||
const ConnectorRow = ({ type, connector, onClickSetup }: Props) => {
|
||||
const ConnectorRow = ({ type, connectors, onClickSetup }: Props) => {
|
||||
const { t } = useTranslation(undefined);
|
||||
|
||||
return (
|
||||
<tr>
|
||||
<td>
|
||||
<ConnectorName type={type} connector={connector} />
|
||||
<ConnectorName
|
||||
type={type}
|
||||
connector={connectors[0]}
|
||||
isShowId={type !== ConnectorType.Social}
|
||||
/>
|
||||
</td>
|
||||
<td>{t(connectorTitlePlaceHolder[type])}</td>
|
||||
<td>
|
||||
{type === ConnectorType.Social && (
|
||||
<Status status={connector?.enabled ? 'enabled' : 'disabled'}>
|
||||
{t(
|
||||
connector?.enabled
|
||||
? 'admin_console.connectors.connector_status_enabled'
|
||||
: 'admin_console.connectors.connector_status_disabled'
|
||||
)}
|
||||
</Status>
|
||||
<div className={styles.statusItems}>
|
||||
{connectors.map(({ id, enabled, platform }) => {
|
||||
const status = enabled ? 'enabled' : 'disabled';
|
||||
|
||||
return (
|
||||
<div key={id} className={styles.statusItem}>
|
||||
<Status status={enabled ? 'enabled' : 'disabled'}>
|
||||
{t(`admin_console.connectors.connector_status_${status}`)}
|
||||
</Status>
|
||||
<div className={styles.platform}>
|
||||
{platform && t(connectorPlatformLabel[platform])}
|
||||
</div>
|
||||
<div className={styles.line} />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
{type !== ConnectorType.Social && connector && (
|
||||
{type !== ConnectorType.Social && connectors[0] && (
|
||||
<Status status="enabled">{t('admin_console.connectors.connector_status_enabled')}</Status>
|
||||
)}
|
||||
{type !== ConnectorType.Social && !connector && (
|
||||
{type !== ConnectorType.Social && !connectors[0] && (
|
||||
<Button
|
||||
title="admin_console.connectors.set_up"
|
||||
type="outline"
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { ConnectorDTO, ConnectorType } from '@logto/schemas';
|
||||
import { ConnectorType } from '@logto/schemas';
|
||||
import classNames from 'classnames';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import Button from '@/components/Button';
|
||||
import Card from '@/components/Card';
|
||||
|
@ -12,7 +11,7 @@ import TabNav, { TabNavItem } from '@/components/TabNav';
|
|||
import TableEmpty from '@/components/Table/TableEmpty';
|
||||
import TableError from '@/components/Table/TableError';
|
||||
import TableLoading from '@/components/Table/TableLoading';
|
||||
import { RequestError } from '@/hooks/use-api';
|
||||
import useConnectorGroups from '@/hooks/use-connector-groups';
|
||||
import Plus from '@/icons/Plus';
|
||||
import * as tableStyles from '@/scss/table.module.scss';
|
||||
|
||||
|
@ -25,20 +24,26 @@ const Connectors = () => {
|
|||
const isSocial = location.pathname === '/connectors/social';
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const [createType, setCreateType] = useState<ConnectorType>();
|
||||
const { data, error, mutate } = useSWR<ConnectorDTO[], RequestError>('/api/connectors');
|
||||
const { data, error, mutate } = useConnectorGroups();
|
||||
const isLoading = !data && !error;
|
||||
|
||||
const emailConnector = useMemo(
|
||||
() => data?.find((connector) => connector.enabled && connector.type === ConnectorType.Email),
|
||||
[data]
|
||||
);
|
||||
const emailConnector = useMemo(() => {
|
||||
const emailConnectorGroup = data?.find(
|
||||
(connector) => connector.enabled && connector.type === ConnectorType.Email
|
||||
);
|
||||
|
||||
const smsConnector = useMemo(
|
||||
() => data?.find((connector) => connector.enabled && connector.type === ConnectorType.SMS),
|
||||
[data]
|
||||
);
|
||||
return emailConnectorGroup?.connectors[0];
|
||||
}, [data]);
|
||||
|
||||
const socialConnectors = useMemo(() => {
|
||||
const smsConnector = useMemo(() => {
|
||||
const smsConnectorGroup = data?.find(
|
||||
(connector) => connector.enabled && connector.type === ConnectorType.SMS
|
||||
);
|
||||
|
||||
return smsConnectorGroup?.connectors[0];
|
||||
}, [data]);
|
||||
|
||||
const socialConnectorGroups = useMemo(() => {
|
||||
if (!isSocial) {
|
||||
return;
|
||||
}
|
||||
|
@ -89,7 +94,7 @@ const Connectors = () => {
|
|||
/>
|
||||
)}
|
||||
{isLoading && <TableLoading columns={3} />}
|
||||
{socialConnectors?.length === 0 && (
|
||||
{socialConnectorGroups?.length === 0 && (
|
||||
<TableEmpty
|
||||
columns={3}
|
||||
title={t('connectors.type.social')}
|
||||
|
@ -100,7 +105,7 @@ const Connectors = () => {
|
|||
)}
|
||||
{!isLoading && !isSocial && (
|
||||
<ConnectorRow
|
||||
connector={emailConnector}
|
||||
connectors={emailConnector ? [emailConnector] : []}
|
||||
type={ConnectorType.Email}
|
||||
onClickSetup={() => {
|
||||
setCreateType(ConnectorType.Email);
|
||||
|
@ -108,14 +113,13 @@ const Connectors = () => {
|
|||
/>
|
||||
)}
|
||||
{!isLoading && !isSocial && (
|
||||
<ConnectorRow connector={smsConnector} type={ConnectorType.SMS} />
|
||||
)}
|
||||
{socialConnectors?.map((connector) => (
|
||||
<ConnectorRow
|
||||
key={connector.id}
|
||||
connector={connector}
|
||||
type={ConnectorType.Social}
|
||||
connectors={smsConnector ? [smsConnector] : []}
|
||||
type={ConnectorType.SMS}
|
||||
/>
|
||||
)}
|
||||
{socialConnectorGroups?.map(({ connectors, id }) => (
|
||||
<ConnectorRow key={id} connectors={connectors} type={ConnectorType.Social} />
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { ConnectorDTO } from '@logto/schemas';
|
||||
|
||||
export type ConnectorGroup = Pick<ConnectorDTO, 'name' | 'logo' | 'target' | 'type'> & {
|
||||
id: string;
|
||||
enabled: boolean;
|
||||
connectors: ConnectorDTO[];
|
||||
};
|
||||
|
|
|
@ -257,6 +257,11 @@ const translation = {
|
|||
subtitle:
|
||||
'A step by step guide to integrate your connector or get a sample configured with your account settings',
|
||||
},
|
||||
platform: {
|
||||
universal: 'Universal',
|
||||
web: 'Web',
|
||||
native: 'Native',
|
||||
},
|
||||
},
|
||||
connector_details: {
|
||||
back_to_connectors: 'Back to Connectors',
|
||||
|
|
|
@ -255,6 +255,11 @@ const translation = {
|
|||
guide: {
|
||||
subtitle: '请参考下列分步指南,配置您的 connector,或点击按钮获取示例配置文件',
|
||||
},
|
||||
platform: {
|
||||
universal: '通用',
|
||||
web: '网页',
|
||||
native: '原生',
|
||||
},
|
||||
},
|
||||
connector_details: {
|
||||
back_to_connectors: '返回连接器',
|
||||
|
|
Loading…
Add table
Reference in a new issue