mirror of
https://github.com/logto-io/logto.git
synced 2024-12-30 20:33:54 -05:00
refactor(console): refactor tag component (#6453)
This commit is contained in:
parent
9674d5c8f0
commit
26b976a828
13 changed files with 56 additions and 71 deletions
|
@ -128,6 +128,7 @@ function CreateForm({
|
||||||
planId === ReservedPlanId.Pro &&
|
planId === ReservedPlanId.Pro &&
|
||||||
ReservedPlanId.Pro
|
ReservedPlanId.Pro
|
||||||
)}
|
)}
|
||||||
|
hasAddOnTag={isDevFeaturesEnabled && watch('type') === ApplicationType.MachineToMachine}
|
||||||
size={defaultCreateType ? 'medium' : 'large'}
|
size={defaultCreateType ? 'medium' : 'large'}
|
||||||
footer={
|
footer={
|
||||||
<Footer
|
<Footer
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
import classNames from 'classnames';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AddOnTag static component
|
|
||||||
*
|
|
||||||
* Used to indicate that a feature is add-on feature and will be charged according to usage.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import styles from './index.module.scss';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
readonly className?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
function AddOnTag({ className }: Props) {
|
|
||||||
return <div className={classNames(styles.tag, styles.beta, className)}>Add-on</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default AddOnTag;
|
|
|
@ -2,11 +2,10 @@ import { ReservedPlanId } from '@logto/schemas';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
|
|
||||||
import { isDevFeaturesEnabled } from '@/consts/env';
|
import { isCloud, isDevFeaturesEnabled } from '@/consts/env';
|
||||||
import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
|
import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
|
||||||
import { TenantsContext } from '@/contexts/TenantsProvider';
|
import { TenantsContext } from '@/contexts/TenantsProvider';
|
||||||
|
|
||||||
import AddOnTag from './AddOnTag';
|
|
||||||
import styles from './index.module.scss';
|
import styles from './index.module.scss';
|
||||||
|
|
||||||
export { default as BetaTag } from './BetaTag';
|
export { default as BetaTag } from './BetaTag';
|
||||||
|
@ -53,9 +52,6 @@ export type Props = {
|
||||||
function FeatureTag(props: Props) {
|
function FeatureTag(props: Props) {
|
||||||
const { className } = props;
|
const { className } = props;
|
||||||
const { isDevTenant } = useContext(TenantsContext);
|
const { isDevTenant } = useContext(TenantsContext);
|
||||||
const {
|
|
||||||
currentSubscription: { planId },
|
|
||||||
} = useContext(SubscriptionDataContext);
|
|
||||||
|
|
||||||
const { isVisible, plan } = props;
|
const { isVisible, plan } = props;
|
||||||
|
|
||||||
|
@ -65,12 +61,36 @@ function FeatureTag(props: Props) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show the add-on tag for Pro plan when dev features are enabled.
|
|
||||||
if (isDevFeaturesEnabled && planId === ReservedPlanId.Pro) {
|
|
||||||
return <AddOnTag className={className} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <div className={classNames(styles.tag, className)}>{plan}</div>;
|
return <div className={classNames(styles.tag, className)}>{plan}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default FeatureTag;
|
export default FeatureTag;
|
||||||
|
|
||||||
|
type CombinedAddOnAndFeatureTagProps = {
|
||||||
|
readonly hasAddOnTag?: boolean;
|
||||||
|
readonly className?: string;
|
||||||
|
/** The minimum plan required to use the feature. */
|
||||||
|
readonly paywall?: Props['plan'];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When `hasAddOnTag` is `true`, the tag will be `AddOnTag` if the plan is `ReservedPlanId.Pro`
|
||||||
|
* and dev features are enabled. Otherwise, it will be `FeatureTag` with the `paywall` prop.
|
||||||
|
*/
|
||||||
|
export function CombinedAddOnAndFeatureTag(props: CombinedAddOnAndFeatureTagProps) {
|
||||||
|
const { hasAddOnTag, className, paywall } = props;
|
||||||
|
const {
|
||||||
|
currentSubscription: { planId },
|
||||||
|
} = useContext(SubscriptionDataContext);
|
||||||
|
|
||||||
|
// Show the "Add-on" tag for Pro plan when dev features enabled.
|
||||||
|
if (hasAddOnTag && isDevFeaturesEnabled && isCloud && planId === ReservedPlanId.Pro) {
|
||||||
|
return <div className={classNames(styles.tag, styles.beta, className)}>Add-on</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (paywall && isCloud) {
|
||||||
|
return <FeatureTag isVisible plan={paywall} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
|
@ -4,8 +4,7 @@ import classNames from 'classnames';
|
||||||
import type { ReactElement } from 'react';
|
import type { ReactElement } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import FeatureTag from '@/components/FeatureTag';
|
import { CombinedAddOnAndFeatureTag } from '@/components/FeatureTag';
|
||||||
import { isCloud } from '@/consts/env';
|
|
||||||
import type { Props as TextLinkProps } from '@/ds-components/TextLink';
|
import type { Props as TextLinkProps } from '@/ds-components/TextLink';
|
||||||
|
|
||||||
import type DangerousRaw from '../DangerousRaw';
|
import type DangerousRaw from '../DangerousRaw';
|
||||||
|
@ -27,6 +26,7 @@ export type Props = {
|
||||||
* If not provided, no paywall tag will be shown.
|
* If not provided, no paywall tag will be shown.
|
||||||
*/
|
*/
|
||||||
readonly paywall?: Exclude<ReservedPlanId, ReservedPlanId.Free | ReservedPlanId.Development>;
|
readonly paywall?: Exclude<ReservedPlanId, ReservedPlanId.Free | ReservedPlanId.Development>;
|
||||||
|
readonly hasAddOnTag?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,6 +40,7 @@ function CardTitle({
|
||||||
learnMoreLink,
|
learnMoreLink,
|
||||||
className,
|
className,
|
||||||
paywall,
|
paywall,
|
||||||
|
hasAddOnTag,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||||
|
|
||||||
|
@ -47,7 +48,7 @@ function CardTitle({
|
||||||
<div className={classNames(styles.container, styles[size], className)}>
|
<div className={classNames(styles.container, styles[size], className)}>
|
||||||
<div className={classNames(styles.title, !isWordWrapEnabled && styles.titleEllipsis)}>
|
<div className={classNames(styles.title, !isWordWrapEnabled && styles.titleEllipsis)}>
|
||||||
{typeof title === 'string' ? <DynamicT forKey={title} /> : title}
|
{typeof title === 'string' ? <DynamicT forKey={title} /> : title}
|
||||||
{paywall && isCloud && <FeatureTag isVisible plan={paywall} />}
|
<CombinedAddOnAndFeatureTag hasAddOnTag={hasAddOnTag} paywall={paywall} />
|
||||||
</div>
|
</div>
|
||||||
{Boolean(subtitle ?? learnMoreLink) && (
|
{Boolean(subtitle ?? learnMoreLink) && (
|
||||||
<div className={styles.subtitle}>
|
<div className={styles.subtitle}>
|
||||||
|
|
|
@ -17,7 +17,10 @@ export type Props = {
|
||||||
readonly className?: string;
|
readonly className?: string;
|
||||||
readonly size?: 'medium' | 'large' | 'xlarge';
|
readonly size?: 'medium' | 'large' | 'xlarge';
|
||||||
readonly headerIcon?: ReactElement;
|
readonly headerIcon?: ReactElement;
|
||||||
} & Pick<CardTitleProps, 'learnMoreLink' | 'title' | 'subtitle' | 'isWordWrapEnabled' | 'paywall'>;
|
} & Pick<
|
||||||
|
CardTitleProps,
|
||||||
|
'learnMoreLink' | 'title' | 'subtitle' | 'isWordWrapEnabled' | 'paywall' | 'hasAddOnTag'
|
||||||
|
>;
|
||||||
|
|
||||||
function ModalLayout({
|
function ModalLayout({
|
||||||
children,
|
children,
|
||||||
|
|
|
@ -68,8 +68,9 @@ function CreateForm({ onClose }: Props) {
|
||||||
title="api_resources.create"
|
title="api_resources.create"
|
||||||
subtitle="api_resources.subtitle"
|
subtitle="api_resources.subtitle"
|
||||||
paywall={conditional(
|
paywall={conditional(
|
||||||
isDevFeaturesEnabled && planId === ReservedPlanId.Pro && ReservedPlanId.Pro
|
isDevFeaturesEnabled && planId !== ReservedPlanId.Pro && ReservedPlanId.Pro
|
||||||
)}
|
)}
|
||||||
|
hasAddOnTag={isDevFeaturesEnabled}
|
||||||
footer={<Footer isCreationLoading={isSubmitting} onClickCreate={onSubmit} />}
|
footer={<Footer isCreationLoading={isSubmitting} onClickCreate={onSubmit} />}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
>
|
>
|
||||||
|
|
|
@ -155,7 +155,7 @@ function SsoCreationModal({ isOpen, onClose: rawOnClose }: Props) {
|
||||||
<ModalLayout
|
<ModalLayout
|
||||||
title="enterprise_sso.create_modal.title"
|
title="enterprise_sso.create_modal.title"
|
||||||
paywall={conditional(
|
paywall={conditional(
|
||||||
isDevFeaturesEnabled && planId === ReservedPlanId.Pro && ReservedPlanId.Pro
|
isDevFeaturesEnabled && planId !== ReservedPlanId.Pro && ReservedPlanId.Pro
|
||||||
)}
|
)}
|
||||||
footer={
|
footer={
|
||||||
conditional(
|
conditional(
|
||||||
|
|
|
@ -36,11 +36,7 @@ function EnterpriseSso() {
|
||||||
const { navigate } = useTenantPathname();
|
const { navigate } = useTenantPathname();
|
||||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||||
const { isDevTenant } = useContext(TenantsContext);
|
const { isDevTenant } = useContext(TenantsContext);
|
||||||
const {
|
const { currentPlan, currentSubscriptionQuota } = useContext(SubscriptionDataContext);
|
||||||
currentPlan,
|
|
||||||
currentSubscription: { planId },
|
|
||||||
currentSubscriptionQuota,
|
|
||||||
} = useContext(SubscriptionDataContext);
|
|
||||||
|
|
||||||
const [{ page }, updateSearchParameters] = useSearchParametersWatcher({
|
const [{ page }, updateSearchParameters] = useSearchParametersWatcher({
|
||||||
page: 1,
|
page: 1,
|
||||||
|
@ -67,11 +63,10 @@ function EnterpriseSso() {
|
||||||
return (
|
return (
|
||||||
<ListPage
|
<ListPage
|
||||||
title={{
|
title={{
|
||||||
paywall: isDevFeaturesEnabled
|
paywall: conditional((!isSsoEnabled || isDevTenant) && ReservedPlanId.Pro),
|
||||||
? conditional(planId === ReservedPlanId.Pro && ReservedPlanId.Pro)
|
|
||||||
: conditional((!isSsoEnabled || isDevTenant) && ReservedPlanId.Pro),
|
|
||||||
title: 'enterprise_sso.title',
|
title: 'enterprise_sso.title',
|
||||||
subtitle: 'enterprise_sso.subtitle',
|
subtitle: 'enterprise_sso.subtitle',
|
||||||
|
hasAddOnTag: isDevFeaturesEnabled,
|
||||||
}}
|
}}
|
||||||
pageMeta={{ titleKey: 'enterprise_sso.page_title' }}
|
pageMeta={{ titleKey: 'enterprise_sso.page_title' }}
|
||||||
createButton={conditional(
|
createButton={conditional(
|
||||||
|
|
|
@ -18,7 +18,6 @@ function PageWrapper({ children }: Props) {
|
||||||
const { isDevTenant } = useContext(TenantsContext);
|
const { isDevTenant } = useContext(TenantsContext);
|
||||||
const {
|
const {
|
||||||
currentPlan,
|
currentPlan,
|
||||||
currentSubscription: { planId },
|
|
||||||
currentSubscriptionQuota: { mfaEnabled },
|
currentSubscriptionQuota: { mfaEnabled },
|
||||||
} = useContext(SubscriptionDataContext);
|
} = useContext(SubscriptionDataContext);
|
||||||
const isMfaEnabled =
|
const isMfaEnabled =
|
||||||
|
@ -28,11 +27,8 @@ function PageWrapper({ children }: Props) {
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<PageMeta titleKey="mfa.title" />
|
<PageMeta titleKey="mfa.title" />
|
||||||
<CardTitle
|
<CardTitle
|
||||||
paywall={
|
paywall={cond((!isMfaEnabled || isDevTenant) && ReservedPlanId.Pro)}
|
||||||
isDevFeaturesEnabled
|
hasAddOnTag={isDevFeaturesEnabled}
|
||||||
? cond(planId === ReservedPlanId.Pro && ReservedPlanId.Pro)
|
|
||||||
: cond((!isMfaEnabled || isDevTenant) && ReservedPlanId.Pro)
|
|
||||||
}
|
|
||||||
title="mfa.title"
|
title="mfa.title"
|
||||||
subtitle="mfa.description"
|
subtitle="mfa.description"
|
||||||
className={styles.cardTitle}
|
className={styles.cardTitle}
|
||||||
|
|
|
@ -32,11 +32,7 @@ const basePathname = '/organization-template';
|
||||||
function OrganizationTemplate() {
|
function OrganizationTemplate() {
|
||||||
const { getDocumentationUrl } = useDocumentationUrl();
|
const { getDocumentationUrl } = useDocumentationUrl();
|
||||||
const [isGuideDrawerOpen, setIsGuideDrawerOpen] = useState(false);
|
const [isGuideDrawerOpen, setIsGuideDrawerOpen] = useState(false);
|
||||||
const {
|
const { currentPlan, currentSubscriptionQuota } = useContext(SubscriptionDataContext);
|
||||||
currentPlan,
|
|
||||||
currentSubscription: { planId },
|
|
||||||
currentSubscriptionQuota,
|
|
||||||
} = useContext(SubscriptionDataContext);
|
|
||||||
const { isDevTenant } = useContext(TenantsContext);
|
const { isDevTenant } = useContext(TenantsContext);
|
||||||
const isOrganizationsDisabled =
|
const isOrganizationsDisabled =
|
||||||
isCloud &&
|
isCloud &&
|
||||||
|
@ -60,11 +56,7 @@ function OrganizationTemplate() {
|
||||||
href: getDocumentationUrl(organizationTemplateLink),
|
href: getDocumentationUrl(organizationTemplateLink),
|
||||||
targetBlank: 'noopener',
|
targetBlank: 'noopener',
|
||||||
}}
|
}}
|
||||||
paywall={
|
paywall={cond((isOrganizationsDisabled || isDevTenant) && ReservedPlanId.Pro)}
|
||||||
isDevFeaturesEnabled
|
|
||||||
? cond(planId === ReservedPlanId.Pro && ReservedPlanId.Pro)
|
|
||||||
: cond((isOrganizationsDisabled || isDevTenant) && ReservedPlanId.Pro)
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
title="application_details.check_guide"
|
title="application_details.check_guide"
|
||||||
|
|
|
@ -82,8 +82,9 @@ function CreateOrganizationModal({ isOpen, onClose }: Props) {
|
||||||
<ModalLayout
|
<ModalLayout
|
||||||
title="organizations.create_organization"
|
title="organizations.create_organization"
|
||||||
paywall={conditional(
|
paywall={conditional(
|
||||||
isDevFeaturesEnabled && planId === ReservedPlanId.Pro && ReservedPlanId.Pro
|
isDevFeaturesEnabled && planId !== ReservedPlanId.Pro && ReservedPlanId.Pro
|
||||||
)}
|
)}
|
||||||
|
hasAddOnTag={isDevFeaturesEnabled}
|
||||||
footer={
|
footer={
|
||||||
cond(
|
cond(
|
||||||
isDevFeaturesEnabled &&
|
isDevFeaturesEnabled &&
|
||||||
|
|
|
@ -25,11 +25,7 @@ const organizationsPathname = '/organizations';
|
||||||
|
|
||||||
function Organizations() {
|
function Organizations() {
|
||||||
const { getDocumentationUrl } = useDocumentationUrl();
|
const { getDocumentationUrl } = useDocumentationUrl();
|
||||||
const {
|
const { currentPlan, currentSubscriptionQuota } = useContext(SubscriptionDataContext);
|
||||||
currentPlan,
|
|
||||||
currentSubscription: { planId },
|
|
||||||
currentSubscriptionQuota,
|
|
||||||
} = useContext(SubscriptionDataContext);
|
|
||||||
const { isDevTenant } = useContext(TenantsContext);
|
const { isDevTenant } = useContext(TenantsContext);
|
||||||
|
|
||||||
const { navigate } = useTenantPathname();
|
const { navigate } = useTenantPathname();
|
||||||
|
@ -64,11 +60,8 @@ function Organizations() {
|
||||||
<PageMeta titleKey="organizations.page_title" />
|
<PageMeta titleKey="organizations.page_title" />
|
||||||
<div className={pageLayout.headline}>
|
<div className={pageLayout.headline}>
|
||||||
<CardTitle
|
<CardTitle
|
||||||
paywall={
|
paywall={cond((isOrganizationsDisabled || isDevTenant) && ReservedPlanId.Pro)}
|
||||||
isDevFeaturesEnabled
|
hasAddOnTag={isDevFeaturesEnabled}
|
||||||
? cond(planId === ReservedPlanId.Pro && ReservedPlanId.Pro)
|
|
||||||
: cond((isOrganizationsDisabled || isDevTenant) && ReservedPlanId.Pro)
|
|
||||||
}
|
|
||||||
title="organizations.title"
|
title="organizations.title"
|
||||||
subtitle="organizations.subtitle"
|
subtitle="organizations.subtitle"
|
||||||
learnMoreLink={{
|
learnMoreLink={{
|
||||||
|
|
|
@ -130,8 +130,9 @@ function InviteMemberModal({ isOpen, onClose }: Props) {
|
||||||
size="large"
|
size="large"
|
||||||
title="tenant_members.invite_modal.title"
|
title="tenant_members.invite_modal.title"
|
||||||
paywall={conditional(
|
paywall={conditional(
|
||||||
isDevFeaturesEnabled && planId === ReservedPlanId.Pro && ReservedPlanId.Pro
|
isDevFeaturesEnabled && planId !== ReservedPlanId.Pro && ReservedPlanId.Pro
|
||||||
)}
|
)}
|
||||||
|
hasAddOnTag={isDevFeaturesEnabled}
|
||||||
subtitle="tenant_members.invite_modal.subtitle"
|
subtitle="tenant_members.invite_modal.subtitle"
|
||||||
footer={
|
footer={
|
||||||
conditional(
|
conditional(
|
||||||
|
|
Loading…
Reference in a new issue