mirror of
https://github.com/logto-io/logto.git
synced 2025-01-27 21:39:16 -05:00
feat(console): connectors (#338)
* feat(console): connectors * refactor: connector row
This commit is contained in:
parent
1df1aee010
commit
88053163e6
17 changed files with 360 additions and 40 deletions
|
@ -39,12 +39,15 @@ const Main = () => {
|
|||
<Content>
|
||||
<Routes>
|
||||
<Route path="api-resources" element={<ApiResources />} />
|
||||
<Route path="connectors" element={<Connectors />} />
|
||||
<Route path="connectors/:connectorId" element={<Connector />} />
|
||||
<Route path="applications">
|
||||
<Route index element={<Applications />} />
|
||||
<Route path=":id" element={<ApplicationDetails />} />
|
||||
</Route>
|
||||
<Route path="connectors">
|
||||
<Route index element={<Connectors />} />
|
||||
<Route path="social" element={<Connectors />} />
|
||||
<Route path=":connectorId" element={<Connector />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
</Content>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
<svg width="245" height="110" viewBox="0 0 245 110" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1640_62321)">
|
||||
<path d="M220.888 44.3013H213.529V69.775H220.888V44.3013Z" fill="#F2F2F2"/>
|
||||
<path d="M225.748 64.9831L207.85 43.6899L171.922 44.0184L150.128 65.2464L150.568 65.5295H150.411V109.684H225.7V65.5295L225.748 64.9831Z" fill="#F2F2F2"/>
|
||||
<path d="M207.868 43.7354L185.508 69.9504V109.684H225.7V64.9634L207.868 43.7354Z" fill="#E6E6E6"/>
|
||||
<path d="M209.85 87.7104H201.641V94.9656H209.85V87.7104Z" fill="#3F3D56"/>
|
||||
<path d="M209.85 75.1528H201.641V82.2929H209.85V75.1528Z" fill="#3F3D56"/>
|
||||
<path d="M209.85 87.7104H201.641V94.9656H209.85V87.7104Z" fill="white"/>
|
||||
<path d="M209.85 75.1528H201.641V82.2929H209.85V75.1528Z" fill="white"/>
|
||||
<path d="M173.372 85.7856H165.164V93.0407H173.372V85.7856Z" fill="white"/>
|
||||
<path d="M173.372 73.2275H165.164V80.3676H173.372V73.2275Z" fill="white"/>
|
||||
<path d="M162.815 44.3013H155.456V69.775H162.815V44.3013Z" fill="#F2F2F2"/>
|
||||
<path d="M167.675 64.9831L149.776 43.6899L113.849 44.0184L92.0542 65.2464L92.4946 65.5295H92.3372V109.684H167.627V65.5295L167.675 64.9831Z" fill="#F2F2F2"/>
|
||||
<path d="M149.795 43.7354L127.435 69.9504V109.684H167.627V64.9634L149.795 43.7354Z" fill="#E6E6E6"/>
|
||||
<path d="M151.776 87.7104H143.568V94.9656H151.776V87.7104Z" fill="#3F3D56"/>
|
||||
<path d="M151.776 75.1528H143.568V82.2929H151.776V75.1528Z" fill="#3F3D56"/>
|
||||
<path d="M151.776 87.7104H143.568V94.9656H151.776V87.7104Z" fill="white"/>
|
||||
<path d="M151.776 75.1528H143.568V82.2929H151.776V75.1528Z" fill="white"/>
|
||||
<path d="M115.298 85.7856H107.09V93.0407H115.298V85.7856Z" fill="white"/>
|
||||
<path d="M115.298 73.2275H107.09V80.3676H115.298V73.2275Z" fill="white"/>
|
||||
<path d="M103.351 22.3169H93.5177V56.356H103.351V22.3169Z" fill="#F2F2F2"/>
|
||||
<path d="M109.845 49.9526L85.9285 21.5L37.9205 21.9387L8.7981 50.3047L9.38639 50.6829H9.17631V109.684H109.781V50.6829L109.845 49.9526Z" fill="#F2F2F2"/>
|
||||
<path d="M85.9533 21.5605L56.0745 56.5903V109.684H109.781V49.9265L85.9533 21.5605Z" fill="#E6E6E6"/>
|
||||
<path d="M88.6009 80.3223H77.6327V90.0169H88.6009V80.3223Z" fill="#3F3D56"/>
|
||||
<path d="M88.6009 63.542H77.6327V73.0829H88.6009V63.542Z" fill="#3F3D56"/>
|
||||
<path d="M88.6009 80.3223H77.6327V90.0169H88.6009V80.3223Z" fill="white"/>
|
||||
<path d="M88.6009 63.542H77.6327V73.0829H88.6009V63.542Z" fill="white"/>
|
||||
<path d="M39.8576 77.75H28.8894V87.4446H39.8576V77.75Z" fill="white"/>
|
||||
<path d="M39.8576 60.9697H28.8894V70.5106H39.8576V60.9697Z" fill="white"/>
|
||||
<path d="M235.968 109.466H0.757812V109.954H235.968V109.466Z" fill="#3F3D56"/>
|
||||
<path d="M37.8156 100.493C41.7371 100.493 44.916 94.0066 44.916 86.0053C44.916 78.004 41.7371 71.5176 37.8156 71.5176C33.8942 71.5176 30.7152 78.004 30.7152 86.0053C30.7152 94.0066 33.8942 100.493 37.8156 100.493Z" fill="#3F3D56"/>
|
||||
<path d="M38.4713 109.72C35.6276 92.6575 38.4427 75.6569 38.4715 75.4873L39.0246 75.581C38.996 75.7498 36.197 92.661 39.0248 109.628L38.4713 109.72Z" fill="#5D34F2"/>
|
||||
<path d="M44.0668 82.8899L37.6461 86.3242L37.9108 86.819L44.3315 83.3847L44.0668 82.8899Z" fill="#5D34F2"/>
|
||||
<path d="M31.1344 84.6102L30.8696 85.105L37.2896 88.5413L37.5544 88.0466L31.1344 84.6102Z" fill="#5D34F2"/>
|
||||
<path d="M20.7554 91.9982C28.4253 91.9982 34.643 79.3116 34.643 63.6619C34.643 48.0123 28.4253 35.3257 20.7554 35.3257C13.0855 35.3257 6.8678 48.0123 6.8678 63.6619C6.8678 79.3116 13.0855 91.9982 20.7554 91.9982Z" fill="#5D34F2"/>
|
||||
<path d="M22.3023 110C16.7476 76.6726 22.2463 43.4663 22.3024 43.1348L22.8556 43.2285C22.7996 43.5592 17.3171 76.6761 22.8557 109.908L22.3023 110Z" fill="#3F3D56"/>
|
||||
<path d="M33.1087 57.8052L20.5503 64.5225L20.815 65.0173L33.3734 58.3L33.1087 57.8052Z" fill="#3F3D56"/>
|
||||
<path d="M7.56131 61.1703L7.29651 61.665L19.8531 68.3858L20.1179 67.8911L7.56131 61.1703Z" fill="#3F3D56"/>
|
||||
<path d="M53.647 85.383C64.1426 85.383 72.6511 68.0224 72.6511 46.607C72.6511 25.1916 64.1426 7.83105 53.647 7.83105C43.1513 7.83105 34.6429 25.1916 34.6429 46.607C34.6429 68.0224 43.1513 85.383 53.647 85.383Z" fill="#5D34F2"/>
|
||||
<path d="M55.8658 110C48.2675 64.4106 55.7891 18.9876 55.866 18.5342L56.4191 18.6279C56.3424 19.0805 48.8369 64.4142 56.4193 109.908L55.8658 110Z" fill="#3F3D56"/>
|
||||
<path d="M70.6003 38.6834L53.4152 47.8755L53.6798 48.3703L70.865 39.1782L70.6003 38.6834Z" fill="#3F3D56"/>
|
||||
<path d="M35.5431 43.2885L35.2783 43.7832L52.4611 52.9798L52.7259 52.4851L35.5431 43.2885Z" fill="#3F3D56"/>
|
||||
<path d="M213.294 100.493C217.216 100.493 220.395 94.0066 220.395 86.0053C220.395 78.004 217.216 71.5176 213.294 71.5176C209.373 71.5176 206.194 78.004 206.194 86.0053C206.194 94.0066 209.373 100.493 213.294 100.493Z" fill="#3F3D56"/>
|
||||
<path d="M212.639 109.72C215.483 92.6575 212.667 75.6569 212.639 75.4873L212.085 75.581C212.114 75.7498 214.913 92.661 212.085 109.628L212.639 109.72Z" fill="#5D34F2"/>
|
||||
<path d="M207.043 82.89L206.779 83.3848L213.199 86.8191L213.464 86.3243L207.043 82.89Z" fill="#5D34F2"/>
|
||||
<path d="M219.976 84.6106L213.556 88.0469L213.82 88.5416L220.24 85.1053L219.976 84.6106Z" fill="#5D34F2"/>
|
||||
<path d="M230.355 91.9982C238.024 91.9982 244.242 79.3116 244.242 63.6619C244.242 48.0123 238.024 35.3257 230.355 35.3257C222.685 35.3257 216.467 48.0123 216.467 63.6619C216.467 79.3116 222.685 91.9982 230.355 91.9982Z" fill="#5D34F2"/>
|
||||
<path d="M228.808 110C234.362 76.6726 228.864 43.4663 228.808 43.1348L228.254 43.2285C228.31 43.5592 233.793 76.6761 228.254 109.908L228.808 110Z" fill="#3F3D56"/>
|
||||
<path d="M218.001 57.805L217.737 58.2998L230.295 65.0171L230.56 64.5223L218.001 57.805Z" fill="#3F3D56"/>
|
||||
<path d="M243.549 61.1703L230.992 67.8911L231.257 68.3858L243.814 61.6651L243.549 61.1703Z" fill="#3F3D56"/>
|
||||
<path d="M197.463 85.383C207.959 85.383 216.467 68.0224 216.467 46.607C216.467 25.1916 207.959 7.83105 197.463 7.83105C186.967 7.83105 178.459 25.1916 178.459 46.607C178.459 68.0224 186.967 85.383 197.463 85.383Z" fill="#5D34F2"/>
|
||||
<path d="M195.244 110C202.843 64.4106 195.321 18.9876 195.244 18.5342L194.691 18.6279C194.768 19.0805 202.273 64.4142 194.691 109.908L195.244 110Z" fill="#3F3D56"/>
|
||||
<path d="M180.51 38.6834L180.245 39.1782L197.43 48.3703L197.695 47.8755L180.51 38.6834Z" fill="#3F3D56"/>
|
||||
<path d="M215.567 43.2882L198.384 52.4849L198.649 52.9796L215.831 43.783L215.567 43.2882Z" fill="#3F3D56"/>
|
||||
<path d="M144.625 24.7583L146.871 22.9617C145.126 22.7692 144.409 23.7209 144.116 24.4741C142.753 23.908 141.268 24.6499 141.268 24.6499L145.763 26.2815C145.536 25.6759 145.141 25.1475 144.625 24.7583Z" fill="#3F3D56"/>
|
||||
<path d="M89.7236 8.8979L91.9699 7.10133C90.2248 6.90881 89.5079 7.8605 89.2144 8.61377C87.8512 8.04768 86.3671 8.78957 86.3671 8.78957L90.8614 10.4212C90.6347 9.81557 90.24 9.28716 89.7236 8.8979Z" fill="#3F3D56"/>
|
||||
<path d="M179.274 1.82173L181.52 0.0251599C179.775 -0.167361 179.058 0.784328 178.765 1.5376C177.401 0.971505 175.917 1.7134 175.917 1.7134L180.412 3.345C180.185 2.7394 179.79 2.21099 179.274 1.82173Z" fill="#3F3D56"/>
|
||||
<path d="M183.809 106.643C183.212 106.643 182.625 106.801 182.108 107.1C181.584 106.625 180.932 106.313 180.233 106.201C179.534 106.089 178.818 106.183 178.171 106.471C177.524 106.758 176.975 107.227 176.59 107.821C176.205 108.415 176 109.108 176.001 109.815H187.213C187.152 108.954 186.767 108.148 186.135 107.56C185.504 106.971 184.672 106.644 183.809 106.643Z" fill="#3F3D56"/>
|
||||
<path d="M69.8581 106.643C69.261 106.643 68.6744 106.801 68.1575 107.1C67.6326 106.625 66.9811 106.313 66.2822 106.201C65.5833 106.089 64.867 106.183 64.2202 106.471C63.5735 106.758 63.0242 107.227 62.6389 107.821C62.2537 108.415 62.0491 109.108 62.0499 109.815H73.2619C73.2008 108.954 72.8157 108.148 72.1841 107.56C71.5526 106.971 70.7214 106.644 69.8581 106.643Z" fill="#3F3D56"/>
|
||||
<path d="M128.176 106.643C127.579 106.643 126.992 106.801 126.475 107.1C125.95 106.625 125.299 106.313 124.6 106.201C123.901 106.089 123.184 106.183 122.538 106.471C121.891 106.758 121.342 107.227 120.956 107.821C120.571 108.415 120.367 109.108 120.367 109.815H131.579C131.518 108.954 131.133 108.148 130.502 107.56C129.87 106.971 129.039 106.644 128.176 106.643Z" fill="#3F3D56"/>
|
||||
<path d="M115.87 98.6343H83.5226V99.6776H88.5674V109.591H89.6108V99.6776H109.26V109.591H110.303V99.6776H115.87V98.6343Z" fill="#3F3D56"/>
|
||||
<path d="M115.934 95.5122H83.5862V96.5557H115.934V95.5122Z" fill="#3F3D56"/>
|
||||
<path d="M115.934 92.9038H83.5862V93.9473H115.934V92.9038Z" fill="#3F3D56"/>
|
||||
<path d="M115.934 90.2949H83.5862V91.3384H115.934V90.2949Z" fill="#3F3D56"/>
|
||||
<path d="M166.379 98.6343H134.032V99.6776H139.077V109.591H140.12V99.6776H159.769V109.591H160.813V99.6776H166.379V98.6343Z" fill="#3F3D56"/>
|
||||
<path d="M166.443 95.5122H134.095V96.5557H166.443V95.5122Z" fill="#3F3D56"/>
|
||||
<path d="M166.443 92.9038H134.095V93.9473H166.443V92.9038Z" fill="#3F3D56"/>
|
||||
<path d="M166.443 90.2949H134.095V91.3384H166.443V90.2949Z" fill="#3F3D56"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1640_62321">
|
||||
<rect width="243.484" height="110" fill="white" transform="translate(0.757812)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 8.7 KiB |
|
@ -40,6 +40,8 @@
|
|||
--color-text-default: #202223;
|
||||
--color-foggy: #888;
|
||||
--color-disabled: #c4c7c7;
|
||||
--color-border: #c4c7c7;
|
||||
--color-green: #66bb6a;
|
||||
}
|
||||
|
||||
$font-family: 'SF UI Text', 'SF Pro Display', sans-serif;
|
||||
|
|
|
@ -24,7 +24,7 @@ const Sidebar = () => {
|
|||
key={title}
|
||||
titleKey={title}
|
||||
icon={<Icon />}
|
||||
isActive={location.pathname === getPath(title)}
|
||||
isActive={location.pathname.startsWith(getPath(title))}
|
||||
/>
|
||||
))}
|
||||
</Section>
|
||||
|
|
18
packages/console/src/components/Status/index.module.scss
Normal file
18
packages/console/src/components/Status/index.module.scss
Normal file
|
@ -0,0 +1,18 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
margin-right: _.unit(2);
|
||||
background: var(--color-green);
|
||||
}
|
||||
|
||||
.status.offline .icon {
|
||||
background: var(--color-neutral-70);
|
||||
}
|
18
packages/console/src/components/Status/index.tsx
Normal file
18
packages/console/src/components/Status/index.tsx
Normal file
|
@ -0,0 +1,18 @@
|
|||
import classNames from 'classnames';
|
||||
import React, { ReactNode } from 'react';
|
||||
|
||||
import styles from './index.module.scss';
|
||||
|
||||
type Props = {
|
||||
status: 'operational' | 'offline';
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
const Status = ({ status, children }: Props) => (
|
||||
<div className={classNames(styles.status, styles[status])}>
|
||||
<div className={styles.icon} />
|
||||
<div>{children}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Status;
|
|
@ -0,0 +1,18 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.link {
|
||||
font-size: var(--font-body-2);
|
||||
margin-right: _.unit(6);
|
||||
padding-bottom: _.unit(1);
|
||||
|
||||
a {
|
||||
color: unset;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.selected {
|
||||
color: var(--color-primary);
|
||||
border-bottom: 1px solid var(--color-primary);
|
||||
margin-bottom: -1px;
|
||||
}
|
23
packages/console/src/components/TabNav/TabNavLink.tsx
Normal file
23
packages/console/src/components/TabNav/TabNavLink.tsx
Normal file
|
@ -0,0 +1,23 @@
|
|||
import classNames from 'classnames';
|
||||
import React from 'react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
|
||||
import * as styles from './TabNavLink.module.scss';
|
||||
|
||||
type Props = {
|
||||
href: string;
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
const TabNavLink = ({ children, href }: Props) => {
|
||||
const location = useLocation();
|
||||
const selected = location.pathname === href;
|
||||
|
||||
return (
|
||||
<div className={classNames(styles.link, selected && styles.selected)}>
|
||||
<Link to={href}>{children}</Link>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TabNavLink;
|
7
packages/console/src/components/TabNav/index.module.scss
Normal file
7
packages/console/src/components/TabNav/index.module.scss
Normal file
|
@ -0,0 +1,7 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.nav {
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
display: flex;
|
||||
margin: _.unit(6) 0 _.unit(1);
|
||||
}
|
13
packages/console/src/components/TabNav/index.tsx
Normal file
13
packages/console/src/components/TabNav/index.tsx
Normal file
|
@ -0,0 +1,13 @@
|
|||
import React from 'react';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
export { default as TabNavLink } from './TabNavLink';
|
||||
|
||||
type Props = {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
const TabNav = ({ children }: Props) => <nav className={styles.nav}>{children}</nav>;
|
||||
|
||||
export default TabNav;
|
|
@ -0,0 +1,10 @@
|
|||
a.link {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 5px;
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
import { ConnectorDTO } from '@logto/schemas';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import ImagePlaceholder from '@/components/ImagePlaceholder';
|
||||
import ItemPreview from '@/components/ItemPreview';
|
||||
|
||||
import * as styles from './ConnectorName.module.scss';
|
||||
|
||||
type Props = {
|
||||
connector?: ConnectorDTO;
|
||||
titlePlaceholder?: string;
|
||||
};
|
||||
|
||||
const ConnectorName = ({ connector, titlePlaceholder = '' }: Props) => {
|
||||
const {
|
||||
i18n: { language },
|
||||
} = useTranslation();
|
||||
|
||||
if (!connector) {
|
||||
return <ItemPreview title={titlePlaceholder} icon={<ImagePlaceholder />} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Link to={`/connectors/${connector.id}`} className={styles.link}>
|
||||
<ItemPreview
|
||||
title={connector.metadata.name[language] ?? connector.metadata.name.en ?? '-'}
|
||||
subtitle={connector.id}
|
||||
icon={
|
||||
connector.metadata.logo.startsWith('http') ? (
|
||||
<img className={styles.logo} src={connector.metadata.logo} />
|
||||
) : (
|
||||
<ImagePlaceholder />
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConnectorName;
|
|
@ -0,0 +1,57 @@
|
|||
import { ConnectorDTO, ConnectorType } from '@logto/schemas';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import Button from '@/components/Button';
|
||||
import Status from '@/components/Status';
|
||||
|
||||
import ConnectorName from './ConnectorName';
|
||||
|
||||
type Props = {
|
||||
type: ConnectorType;
|
||||
connector?: ConnectorDTO;
|
||||
};
|
||||
|
||||
const ConnectorRow = ({ type, connector }: Props) => {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
|
||||
const typeLabel = useMemo(() => {
|
||||
if (type === ConnectorType.Email) {
|
||||
return t('connectors.type.email');
|
||||
}
|
||||
|
||||
if (type === ConnectorType.SMS) {
|
||||
return t('connectors.type.sms');
|
||||
}
|
||||
|
||||
return t('connectors.type.social');
|
||||
}, [type, t]);
|
||||
|
||||
return (
|
||||
<tr>
|
||||
<td>
|
||||
<ConnectorName connector={connector} titlePlaceholder={typeLabel} />
|
||||
</td>
|
||||
<td>{typeLabel}</td>
|
||||
<td>
|
||||
{type === ConnectorType.Social && (
|
||||
<Status status={connector?.enabled ? 'operational' : 'offline'}>
|
||||
{t(
|
||||
connector?.enabled
|
||||
? 'connectors.connector_status_enabled'
|
||||
: 'connectors.connector_status_disabled'
|
||||
)}
|
||||
</Status>
|
||||
)}
|
||||
{type !== ConnectorType.Social && connector && (
|
||||
<Status status="operational">{t('connectors.connector_status_enabled')}</Status>
|
||||
)}
|
||||
{type !== ConnectorType.Social && !connector && (
|
||||
<Button title="admin_console.connectors.set_up" />
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConnectorRow;
|
|
@ -8,6 +8,7 @@
|
|||
.table {
|
||||
margin-top: _.unit(6);
|
||||
width: 100%;
|
||||
font-size: var(--font-small-text);
|
||||
|
||||
tbody tr.clickable {
|
||||
cursor: pointer;
|
||||
|
@ -22,13 +23,11 @@
|
|||
width: 360px;
|
||||
}
|
||||
|
||||
a.link {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
.empty {
|
||||
text-align: center;
|
||||
font-size: var(--font-body-2);
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 5px;
|
||||
.emptyLine {
|
||||
padding: _.unit(2) 0;
|
||||
}
|
||||
|
|
|
@ -1,70 +1,95 @@
|
|||
import { ConnectorDTO } from '@logto/schemas';
|
||||
import React from 'react';
|
||||
import { ConnectorDTO, ConnectorType } from '@logto/schemas';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import placeholder from '@/assets/images/social-connectors-placeholder.svg';
|
||||
import Button from '@/components/Button';
|
||||
import Card from '@/components/Card';
|
||||
import CardTitle from '@/components/CardTitle';
|
||||
import ImagePlaceholder from '@/components/ImagePlaceholder';
|
||||
import ItemPreview from '@/components/ItemPreview';
|
||||
import TabNav, { TabNavLink } from '@/components/TabNav';
|
||||
import { RequestError } from '@/swr';
|
||||
|
||||
import ConnectorRow from './components/ConnectorRow';
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
const Connectors = () => {
|
||||
const { t, i18n } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const location = useLocation();
|
||||
const isSocial = location.pathname === '/connectors/social';
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const { data, error } = useSWR<ConnectorDTO[], RequestError>('/api/connectors');
|
||||
const isLoading = !data && !error;
|
||||
|
||||
const emailConnector = useMemo(
|
||||
() => data?.find((connector) => connector.metadata.type === ConnectorType.Email),
|
||||
[data]
|
||||
);
|
||||
|
||||
const smsConnector = useMemo(
|
||||
() => data?.find((connector) => connector.metadata.type === ConnectorType.SMS),
|
||||
[data]
|
||||
);
|
||||
|
||||
const socialConnectors = useMemo(() => {
|
||||
if (!isSocial) {
|
||||
return;
|
||||
}
|
||||
|
||||
return data?.filter((connector) => connector.metadata.type === ConnectorType.Social);
|
||||
}, [data, isSocial]);
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<div className={styles.headline}>
|
||||
<CardTitle title="connectors.title" subtitle="connectors.subtitle" />
|
||||
<Button disabled title="admin_console.connectors.create" />
|
||||
{isSocial && <Button disabled title="admin_console.connectors.create" />}
|
||||
</div>
|
||||
<TabNav>
|
||||
<TabNavLink href="/connectors">{t('connectors.tab_email_sms')}</TabNavLink>
|
||||
<TabNavLink href="/connectors/social">{t('connectors.tab_social')}</TabNavLink>
|
||||
</TabNav>
|
||||
<table className={styles.table}>
|
||||
<thead>
|
||||
<tr>
|
||||
<td className={styles.connectorName}>{t('connectors.connector_name')}</td>
|
||||
<td>{t('connectors.connector_type')}</td>
|
||||
<td>{t('connectors.connector_status')}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{error && (
|
||||
<tr>
|
||||
<td colSpan={2}>error occurred: {error.metadata.code}</td>
|
||||
<td colSpan={3}>error occurred: {error.metadata.code}</td>
|
||||
</tr>
|
||||
)}
|
||||
{isLoading && (
|
||||
<tr>
|
||||
<td colSpan={2}>loading</td>
|
||||
<td colSpan={3}>loading</td>
|
||||
</tr>
|
||||
)}
|
||||
{data?.map(({ id, metadata: { name, logo }, enabled }) => (
|
||||
<tr key={id}>
|
||||
<td>
|
||||
<Link to={`/connectors/${id}`} className={styles.link}>
|
||||
<ItemPreview
|
||||
title={name[i18n.language] ?? name.en ?? '-'}
|
||||
subtitle={id}
|
||||
icon={
|
||||
logo.startsWith('http') ? (
|
||||
<img className={styles.logo} src={logo} />
|
||||
) : (
|
||||
<ImagePlaceholder />
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Link>
|
||||
</td>
|
||||
<td>
|
||||
{enabled
|
||||
? t('connectors.connector_status_enabled')
|
||||
: t('connectors.connector_status_disabled')}
|
||||
{socialConnectors?.length === 0 && (
|
||||
<tr>
|
||||
<td colSpan={3}>
|
||||
<div className={styles.empty}>
|
||||
<div>
|
||||
<img src={placeholder} />
|
||||
</div>
|
||||
<div className={styles.emptyLine}>{t('connectors.type.social')}</div>
|
||||
<div className={styles.emptyLine}>{t('connectors.social_connector_eg')}</div>
|
||||
<Button disabled title="admin_console.connectors.create" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{!isLoading && !isSocial && (
|
||||
<ConnectorRow connector={emailConnector} type={ConnectorType.Email} />
|
||||
)}
|
||||
{!isLoading && !isSocial && (
|
||||
<ConnectorRow connector={smsConnector} type={ConnectorType.SMS} />
|
||||
)}
|
||||
{socialConnectors?.map((connector) => (
|
||||
<ConnectorRow key={connector.id} connector={connector} type={ConnectorType.Social} />
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -83,10 +83,15 @@ const translation = {
|
|||
title: 'Connectors',
|
||||
subtitle: 'Setup connectors to enable passwordless and social sign in experience.',
|
||||
create: 'Add Connector',
|
||||
set_up: 'Set Up',
|
||||
tab_email_sms: 'Email and SMS connectors',
|
||||
tab_social: 'Social connectors',
|
||||
connector_name: 'Connector Name',
|
||||
connector_type: 'Type',
|
||||
connector_status: 'Status',
|
||||
connector_status_enabled: 'Enabled',
|
||||
connector_status_disabled: 'Disabled',
|
||||
social_connector_eg: 'e.g.: Google, Facebook, Twitter',
|
||||
type: {
|
||||
email: 'Email Sender',
|
||||
sms: 'SMS Sender',
|
||||
|
|
|
@ -85,10 +85,15 @@ const translation = {
|
|||
title: '连接器',
|
||||
subtitle: 'Setup connectors to enable passwordless and social sign in experience.',
|
||||
create: '添加连接器',
|
||||
set_up: '设置',
|
||||
tab_email_sms: '邮件/短信服务商',
|
||||
tab_social: '社会化登录',
|
||||
connector_name: '连接器',
|
||||
connector_type: '类型',
|
||||
connector_status: '状态',
|
||||
connector_status_enabled: '已启用',
|
||||
connector_status_disabled: '已禁用',
|
||||
social_connector_eg: '如: 微信登录,支付宝登录,微博登录',
|
||||
type: {
|
||||
email: '邮件服务商',
|
||||
sms: '短信服务商',
|
||||
|
|
Loading…
Add table
Reference in a new issue