mirror of
https://github.com/logto-io/logto.git
synced 2024-12-30 20:33:54 -05:00
feat(console): implement tag component (#3853)
This commit is contained in:
parent
21d1fa42c7
commit
05b3729260
17 changed files with 153 additions and 122 deletions
|
@ -1,32 +0,0 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font: var(--font-body-2);
|
||||
|
||||
.icon {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
margin-right: _.unit(2);
|
||||
background: var(--color-success-70);
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
.icon {
|
||||
background: var(--color-neutral-70);
|
||||
}
|
||||
}
|
||||
|
||||
&.outlined {
|
||||
padding: _.unit(0.5) _.unit(2);
|
||||
border-radius: 10px;
|
||||
background: var(--color-success-99);
|
||||
font: var(--font-label-3);
|
||||
|
||||
&.disabled {
|
||||
background: var(--color-neutral-variant-95);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
import classNames from 'classnames';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
type Props = {
|
||||
status: 'enabled' | 'disabled';
|
||||
children: ReactNode;
|
||||
variant?: 'plain' | 'outlined';
|
||||
};
|
||||
|
||||
function Status({ status, children, variant = 'plain' }: Props) {
|
||||
return (
|
||||
<div className={classNames(styles.status, styles[status], styles[variant])}>
|
||||
<div className={styles.icon} />
|
||||
<div>{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Status;
|
70
packages/console/src/components/Tag/index.module.scss
Normal file
70
packages/console/src/components/Tag/index.module.scss
Normal file
|
@ -0,0 +1,70 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.tag {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font: var(--font-body-2);
|
||||
|
||||
.icon {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
margin-right: _.unit(2);
|
||||
background: var(--color-on-success-container);
|
||||
}
|
||||
|
||||
.resultIcon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: unset;
|
||||
background-color: unset;
|
||||
color: var(--color-on-success-container);
|
||||
}
|
||||
|
||||
&.info {
|
||||
.icon {
|
||||
background: var(--color-on-info-container);
|
||||
}
|
||||
}
|
||||
|
||||
&.alert {
|
||||
.icon {
|
||||
background: var(--color-on-alert-container);
|
||||
}
|
||||
}
|
||||
|
||||
&.error {
|
||||
.icon {
|
||||
background: var(--color-on-error-container);
|
||||
}
|
||||
|
||||
.resultIcon {
|
||||
background: unset;
|
||||
color: var(--color-on-error-container);
|
||||
}
|
||||
}
|
||||
|
||||
&.outlined {
|
||||
padding: _.unit(0.5) _.unit(2);
|
||||
border-radius: 10px;
|
||||
background: var(--color-success-container);
|
||||
font: var(--font-label-3);
|
||||
|
||||
&.info {
|
||||
background: var(--color-info-container);
|
||||
}
|
||||
|
||||
&.alert {
|
||||
background: var(--color-alert-container);
|
||||
}
|
||||
|
||||
&.error {
|
||||
background: var(--color-error-container);
|
||||
|
||||
.resultIcon {
|
||||
background: unset;
|
||||
color: var(--color-on-error-container);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
41
packages/console/src/components/Tag/index.tsx
Normal file
41
packages/console/src/components/Tag/index.tsx
Normal file
|
@ -0,0 +1,41 @@
|
|||
import { conditional } from '@silverhand/essentials';
|
||||
import classNames from 'classnames';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
import Failed from '@/assets/images/failed.svg';
|
||||
import Success from '@/assets/images/success.svg';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
type Props = {
|
||||
type?: 'property' | 'state' | 'result';
|
||||
status?: 'info' | 'success' | 'alert' | 'error';
|
||||
variant?: 'plain' | 'outlined';
|
||||
className?: string;
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
const ResultIconMap: Partial<Record<Required<Props>['status'], SvgComponent>> = {
|
||||
success: Success,
|
||||
error: Failed,
|
||||
};
|
||||
|
||||
function Tag({
|
||||
type = 'property',
|
||||
status = 'info',
|
||||
variant = 'outlined',
|
||||
className,
|
||||
children,
|
||||
}: Props) {
|
||||
const ResultIcon = conditional(type === 'result' && ResultIconMap[status]);
|
||||
|
||||
return (
|
||||
<div className={classNames(styles.tag, styles[status], styles[variant], className)}>
|
||||
{type === 'state' && <div className={styles.icon} />}
|
||||
{ResultIcon && <ResultIcon className={classNames(styles.icon, styles.resultIcon)} />}
|
||||
<div>{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Tag;
|
|
@ -67,14 +67,6 @@
|
|||
margin-left: _.unit(2);
|
||||
}
|
||||
|
||||
.type {
|
||||
background-color: var(--color-surface-variant);
|
||||
color: var(--color-text);
|
||||
padding: _.unit(0.5) _.unit(2);
|
||||
border-radius: 10px;
|
||||
font: var(--font-label-3);
|
||||
}
|
||||
|
||||
.text {
|
||||
font: var(--font-label-2);
|
||||
color: var(--color-text-secondary);
|
||||
|
|
|
@ -21,6 +21,7 @@ import DetailsPage from '@/components/DetailsPage';
|
|||
import Drawer from '@/components/Drawer';
|
||||
import PageMeta from '@/components/PageMeta';
|
||||
import TabNav, { TabNavItem } from '@/components/TabNav';
|
||||
import Tag from '@/components/Tag';
|
||||
import UnsavedChangesAlertModal from '@/components/UnsavedChangesAlertModal';
|
||||
import type { RequestError } from '@/hooks/use-api';
|
||||
import useApi from '@/hooks/use-api';
|
||||
|
@ -147,7 +148,7 @@ function ApplicationDetails() {
|
|||
<div className={styles.metadata}>
|
||||
<div className={styles.name}>{data.name}</div>
|
||||
<div className={styles.details}>
|
||||
<div className={styles.type}>{t(`${applicationTypeI18nKey[data.type]}.title`)}</div>
|
||||
<Tag>{t(`${applicationTypeI18nKey[data.type]}.title`)}</Tag>
|
||||
<div className={styles.verticalBar} />
|
||||
<div className={styles.text}>App ID</div>
|
||||
<CopyToClipboard size="small" value={data.id} />
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.connectorType {
|
||||
background: var(--color-surface-variant);
|
||||
border-radius: 10px;
|
||||
padding: _.unit(0.5) _.unit(2);
|
||||
color: var(--color-text);
|
||||
font: var(--font-label-3);
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import { ConnectorType } from '@logto/schemas';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
import Tag from '@/components/Tag';
|
||||
|
||||
type Props = {
|
||||
type: ConnectorType;
|
||||
|
@ -11,11 +11,11 @@ function ConnectorTypeName({ type }: Props) {
|
|||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
|
||||
return (
|
||||
<div className={styles.connectorType}>
|
||||
<Tag>
|
||||
{type === ConnectorType.Email && t('connector_details.type_email')}
|
||||
{type === ConnectorType.Sms && t('connector_details.type_sms')}
|
||||
{type === ConnectorType.Social && t('connector_details.type_social')}
|
||||
</div>
|
||||
</Tag>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,8 +19,8 @@ import DetailsPage from '@/components/DetailsPage';
|
|||
import Drawer from '@/components/Drawer';
|
||||
import Markdown from '@/components/Markdown';
|
||||
import PageMeta from '@/components/PageMeta';
|
||||
import Status from '@/components/Status';
|
||||
import TabNav, { TabNavItem } from '@/components/TabNav';
|
||||
import Tag from '@/components/Tag';
|
||||
import UnnamedTrans from '@/components/UnnamedTrans';
|
||||
import { ConnectorsTabs } from '@/consts/page-tabs';
|
||||
import type { RequestError } from '@/hooks/use-api';
|
||||
|
@ -129,19 +129,19 @@ function ConnectorDetails() {
|
|||
<div className={styles.verticalBar} />
|
||||
{connectorFactory && (
|
||||
<>
|
||||
<div className={styles.factoryName}>
|
||||
<Tag>
|
||||
<UnnamedTrans resource={connectorFactory.name} />
|
||||
</div>
|
||||
</Tag>
|
||||
<div className={styles.verticalBar} />
|
||||
</>
|
||||
)}
|
||||
<Status status={inUse ? 'enabled' : 'disabled'} variant="outlined">
|
||||
<Tag type="state" status={inUse ? 'success' : 'info'}>
|
||||
{t(
|
||||
inUse
|
||||
? 'connectors.connector_status_in_use'
|
||||
: 'connectors.connector_status_not_in_use'
|
||||
)}
|
||||
</Status>
|
||||
</Tag>
|
||||
<div className={styles.verticalBar} />
|
||||
<div className={styles.text}>ID</div>
|
||||
<CopyToClipboard size="small" value={data.id} />
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import { ConnectorType } from '@logto/connector-kit';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import Tag from '@/components/Tag';
|
||||
import { Tooltip } from '@/components/Tip';
|
||||
|
||||
import * as styles from './DemoTag.module.scss';
|
||||
|
||||
type Props = {
|
||||
connectorType: ConnectorType;
|
||||
};
|
||||
|
@ -17,7 +16,7 @@ function DemoTag({ connectorType }: Props) {
|
|||
<Tooltip
|
||||
content={<div>{t(isSocial ? 'connectors.social_demo_tip' : 'connectors.demo_tip')}</div>}
|
||||
>
|
||||
<div className={styles.tag}>{t('general.demo')}</div>
|
||||
<Tag status="alert">{t('general.demo')}</Tag>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import Status from '@/components/Status';
|
||||
import Tag from '@/components/Tag';
|
||||
import useConnectorInUse from '@/hooks/use-connector-in-use';
|
||||
import type { ConnectorGroup } from '@/types/connector';
|
||||
|
||||
|
@ -18,9 +18,9 @@ function ConnectorStatus({ connectorGroup }: Props) {
|
|||
const inUse = isConnectorInUse(firstConnector);
|
||||
|
||||
return firstConnector ? (
|
||||
<Status status={inUse ? 'enabled' : 'disabled'}>
|
||||
<Tag type="state" status={inUse ? 'success' : 'info'} variant="plain">
|
||||
{t(inUse ? 'connectors.connector_status_in_use' : 'connectors.connector_status_not_in_use')}
|
||||
</Status>
|
||||
</Tag>
|
||||
) : (
|
||||
<span>-</span>
|
||||
);
|
||||
|
|
|
@ -17,19 +17,10 @@
|
|||
align-items: center;
|
||||
|
||||
> span {
|
||||
margin-left: _.unit(2);
|
||||
margin: _.unit(2);
|
||||
font: var(--font-body-2);
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.builtInFlag {
|
||||
display: inline-block;
|
||||
font: var(--font-label-3);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-surface-variant);
|
||||
padding: _.unit(0.5) _.unit(2);
|
||||
border-radius: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ import Button from '@/components/Button';
|
|||
import ConfirmModal from '@/components/ConfirmModal';
|
||||
import IconButton from '@/components/IconButton';
|
||||
import Table from '@/components/Table';
|
||||
import Tag from '@/components/Tag';
|
||||
import Textarea from '@/components/Textarea';
|
||||
import { Tooltip } from '@/components/Tip';
|
||||
import useApi, { RequestError } from '@/hooks/use-api';
|
||||
|
@ -157,11 +158,7 @@ function LanguageDetails() {
|
|||
<div className={styles.languageInfo}>
|
||||
{uiLanguageNameMapping[selectedLanguage]}
|
||||
<span>{selectedLanguage}</span>
|
||||
{isBuiltIn && (
|
||||
<span className={styles.builtInFlag}>
|
||||
{t('sign_in_exp.others.manage_language.logto_provided')}
|
||||
</span>
|
||||
)}
|
||||
{isBuiltIn && <Tag>{t('sign_in_exp.others.manage_language.logto_provided')}</Tag>}
|
||||
</div>
|
||||
{!isBuiltIn && (
|
||||
<Tooltip content={t('sign_in_exp.others.manage_language.deletion_tip')}>
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.suspended {
|
||||
background: var(--color-error-container);
|
||||
color: var(--color-text);
|
||||
font: var(--font-label-3);
|
||||
padding: _.unit(0.5) _.unit(1.5);
|
||||
border-radius: 10px;
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
import classNames from 'classnames';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
import Tag from '@/components/Tag';
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
|
@ -11,7 +10,9 @@ function SuspendedTag({ className }: Props) {
|
|||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
|
||||
return (
|
||||
<div className={classNames(styles.suspended, className)}>{t('user_details.suspended')}</div>
|
||||
<Tag status="error" className={className}>
|
||||
{t('user_details.suspended')}
|
||||
</Tag>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,8 +20,8 @@ import DeleteConfirmModal from '@/components/DeleteConfirmModal';
|
|||
import DetailsPage from '@/components/DetailsPage';
|
||||
import DynamicT from '@/components/DynamicT';
|
||||
import PageMeta from '@/components/PageMeta';
|
||||
import Status from '@/components/Status';
|
||||
import TabNav, { TabNavItem } from '@/components/TabNav';
|
||||
import Tag from '@/components/Tag';
|
||||
import { WebhookDetailsTabs } from '@/consts';
|
||||
import useApi, { type RequestError } from '@/hooks/use-api';
|
||||
import useTheme from '@/hooks/use-theme';
|
||||
|
@ -108,9 +108,9 @@ function WebhookDetails() {
|
|||
{isEnabled ? (
|
||||
<div>Success Rate (WIP)</div>
|
||||
) : (
|
||||
<Status status="disabled" variant="outlined">
|
||||
<Tag type="state" status="info">
|
||||
<DynamicT forKey="webhook_details.not_in_use" />
|
||||
</Status>
|
||||
</Tag>
|
||||
)}
|
||||
<div className={styles.verticalBar} />
|
||||
<div className={styles.text}>ID</div>
|
||||
|
|
|
@ -105,10 +105,15 @@
|
|||
--color-tertiary-container: var(--color-tertiary-90);
|
||||
--color-on-tertiary-container: var(--color-tertiary-10);
|
||||
--color-error: var(--color-error-40);
|
||||
--color-on-error: var(--color-all-100);
|
||||
--color-error-container: var(--color-error-90);
|
||||
--color-on-error-container: var(--color-error-10);
|
||||
--color-alert-container: var(--color-alert-99);
|
||||
--color-error-hover: var(--color-error-50);
|
||||
--color-error-container: var(--color-error-95);
|
||||
--color-on-error-container: var(--color-error-50);
|
||||
--color-alert-container: var(--color-alert-95);
|
||||
--color-on-alert-container: var(--color-alert-70);
|
||||
--color-success-container: var(--color-success-99);
|
||||
--color-on-success-container: var(--color-success-70);
|
||||
--color-info-container: var(--color-neutral-variant-90);
|
||||
--color-on-info-container: var(--color-neutral-variant-60);
|
||||
--color-background: var(--color-neutral-99);
|
||||
--color-on-background: var(--color-neutral-10);
|
||||
--color-surface: var(--color-neutral-99);
|
||||
|
@ -278,10 +283,15 @@
|
|||
--color-tertiary-container: var(--color-tertiary-90);
|
||||
--color-on-tertiary-container: var(--color-tertiary-30);
|
||||
--color-error: var(--color-error-70);
|
||||
--color-on-error: var(--color-all-0);
|
||||
--color-error-container: var(--color-error-90);
|
||||
--color-on-error-container: var(--color-error-30);
|
||||
--color-error-hover: var(--color-error-60);
|
||||
--color-error-container: var(--color-error-95);
|
||||
--color-on-error-container: var(--color-error-70);
|
||||
--color-alert-container: var(--color-alert-90);
|
||||
--color-on-alert-container: var(--color-alert-60);
|
||||
--color-success-container: var(--color-success-90);
|
||||
--color-on-success-container: var(--color-success-60);
|
||||
--color-info-container: var(--color-neutral-variant-90);
|
||||
--color-on-info-container: var(--color-neutral-variant-70);
|
||||
--color-background: var(--color-neutral-99);
|
||||
--color-on-background: var(--color-neutral-10);
|
||||
--color-surface: var(--color-neutral-99);
|
||||
|
|
Loading…
Reference in a new issue