0
Fork 0
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:
Darcy Ye 2024-08-16 14:11:47 +08:00 committed by GitHub
parent 9674d5c8f0
commit 26b976a828
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 56 additions and 71 deletions

View file

@ -128,6 +128,7 @@ function CreateForm({
planId === ReservedPlanId.Pro &&
ReservedPlanId.Pro
)}
hasAddOnTag={isDevFeaturesEnabled && watch('type') === ApplicationType.MachineToMachine}
size={defaultCreateType ? 'medium' : 'large'}
footer={
<Footer

View file

@ -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;

View file

@ -2,11 +2,10 @@ import { ReservedPlanId } from '@logto/schemas';
import classNames from 'classnames';
import { useContext } from 'react';
import { isDevFeaturesEnabled } from '@/consts/env';
import { isCloud, isDevFeaturesEnabled } from '@/consts/env';
import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
import { TenantsContext } from '@/contexts/TenantsProvider';
import AddOnTag from './AddOnTag';
import styles from './index.module.scss';
export { default as BetaTag } from './BetaTag';
@ -53,9 +52,6 @@ export type Props = {
function FeatureTag(props: Props) {
const { className } = props;
const { isDevTenant } = useContext(TenantsContext);
const {
currentSubscription: { planId },
} = useContext(SubscriptionDataContext);
const { isVisible, plan } = props;
@ -65,12 +61,36 @@ function FeatureTag(props: Props) {
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>;
}
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;
}

View file

@ -4,8 +4,7 @@ import classNames from 'classnames';
import type { ReactElement } from 'react';
import { useTranslation } from 'react-i18next';
import FeatureTag from '@/components/FeatureTag';
import { isCloud } from '@/consts/env';
import { CombinedAddOnAndFeatureTag } from '@/components/FeatureTag';
import type { Props as TextLinkProps } from '@/ds-components/TextLink';
import type DangerousRaw from '../DangerousRaw';
@ -27,6 +26,7 @@ export type Props = {
* If not provided, no paywall tag will be shown.
*/
readonly paywall?: Exclude<ReservedPlanId, ReservedPlanId.Free | ReservedPlanId.Development>;
readonly hasAddOnTag?: boolean;
};
/**
@ -40,6 +40,7 @@ function CardTitle({
learnMoreLink,
className,
paywall,
hasAddOnTag,
}: Props) {
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.title, !isWordWrapEnabled && styles.titleEllipsis)}>
{typeof title === 'string' ? <DynamicT forKey={title} /> : title}
{paywall && isCloud && <FeatureTag isVisible plan={paywall} />}
<CombinedAddOnAndFeatureTag hasAddOnTag={hasAddOnTag} paywall={paywall} />
</div>
{Boolean(subtitle ?? learnMoreLink) && (
<div className={styles.subtitle}>

View file

@ -17,7 +17,10 @@ export type Props = {
readonly className?: string;
readonly size?: 'medium' | 'large' | 'xlarge';
readonly headerIcon?: ReactElement;
} & Pick<CardTitleProps, 'learnMoreLink' | 'title' | 'subtitle' | 'isWordWrapEnabled' | 'paywall'>;
} & Pick<
CardTitleProps,
'learnMoreLink' | 'title' | 'subtitle' | 'isWordWrapEnabled' | 'paywall' | 'hasAddOnTag'
>;
function ModalLayout({
children,

View file

@ -68,8 +68,9 @@ function CreateForm({ onClose }: Props) {
title="api_resources.create"
subtitle="api_resources.subtitle"
paywall={conditional(
isDevFeaturesEnabled && planId === ReservedPlanId.Pro && ReservedPlanId.Pro
isDevFeaturesEnabled && planId !== ReservedPlanId.Pro && ReservedPlanId.Pro
)}
hasAddOnTag={isDevFeaturesEnabled}
footer={<Footer isCreationLoading={isSubmitting} onClickCreate={onSubmit} />}
onClose={onClose}
>

View file

@ -155,7 +155,7 @@ function SsoCreationModal({ isOpen, onClose: rawOnClose }: Props) {
<ModalLayout
title="enterprise_sso.create_modal.title"
paywall={conditional(
isDevFeaturesEnabled && planId === ReservedPlanId.Pro && ReservedPlanId.Pro
isDevFeaturesEnabled && planId !== ReservedPlanId.Pro && ReservedPlanId.Pro
)}
footer={
conditional(

View file

@ -36,11 +36,7 @@ function EnterpriseSso() {
const { navigate } = useTenantPathname();
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const { isDevTenant } = useContext(TenantsContext);
const {
currentPlan,
currentSubscription: { planId },
currentSubscriptionQuota,
} = useContext(SubscriptionDataContext);
const { currentPlan, currentSubscriptionQuota } = useContext(SubscriptionDataContext);
const [{ page }, updateSearchParameters] = useSearchParametersWatcher({
page: 1,
@ -67,11 +63,10 @@ function EnterpriseSso() {
return (
<ListPage
title={{
paywall: isDevFeaturesEnabled
? conditional(planId === ReservedPlanId.Pro && ReservedPlanId.Pro)
: conditional((!isSsoEnabled || isDevTenant) && ReservedPlanId.Pro),
paywall: conditional((!isSsoEnabled || isDevTenant) && ReservedPlanId.Pro),
title: 'enterprise_sso.title',
subtitle: 'enterprise_sso.subtitle',
hasAddOnTag: isDevFeaturesEnabled,
}}
pageMeta={{ titleKey: 'enterprise_sso.page_title' }}
createButton={conditional(

View file

@ -18,7 +18,6 @@ function PageWrapper({ children }: Props) {
const { isDevTenant } = useContext(TenantsContext);
const {
currentPlan,
currentSubscription: { planId },
currentSubscriptionQuota: { mfaEnabled },
} = useContext(SubscriptionDataContext);
const isMfaEnabled =
@ -28,11 +27,8 @@ function PageWrapper({ children }: Props) {
<div className={styles.container}>
<PageMeta titleKey="mfa.title" />
<CardTitle
paywall={
isDevFeaturesEnabled
? cond(planId === ReservedPlanId.Pro && ReservedPlanId.Pro)
: cond((!isMfaEnabled || isDevTenant) && ReservedPlanId.Pro)
}
paywall={cond((!isMfaEnabled || isDevTenant) && ReservedPlanId.Pro)}
hasAddOnTag={isDevFeaturesEnabled}
title="mfa.title"
subtitle="mfa.description"
className={styles.cardTitle}

View file

@ -32,11 +32,7 @@ const basePathname = '/organization-template';
function OrganizationTemplate() {
const { getDocumentationUrl } = useDocumentationUrl();
const [isGuideDrawerOpen, setIsGuideDrawerOpen] = useState(false);
const {
currentPlan,
currentSubscription: { planId },
currentSubscriptionQuota,
} = useContext(SubscriptionDataContext);
const { currentPlan, currentSubscriptionQuota } = useContext(SubscriptionDataContext);
const { isDevTenant } = useContext(TenantsContext);
const isOrganizationsDisabled =
isCloud &&
@ -60,11 +56,7 @@ function OrganizationTemplate() {
href: getDocumentationUrl(organizationTemplateLink),
targetBlank: 'noopener',
}}
paywall={
isDevFeaturesEnabled
? cond(planId === ReservedPlanId.Pro && ReservedPlanId.Pro)
: cond((isOrganizationsDisabled || isDevTenant) && ReservedPlanId.Pro)
}
paywall={cond((isOrganizationsDisabled || isDevTenant) && ReservedPlanId.Pro)}
/>
<Button
title="application_details.check_guide"

View file

@ -82,8 +82,9 @@ function CreateOrganizationModal({ isOpen, onClose }: Props) {
<ModalLayout
title="organizations.create_organization"
paywall={conditional(
isDevFeaturesEnabled && planId === ReservedPlanId.Pro && ReservedPlanId.Pro
isDevFeaturesEnabled && planId !== ReservedPlanId.Pro && ReservedPlanId.Pro
)}
hasAddOnTag={isDevFeaturesEnabled}
footer={
cond(
isDevFeaturesEnabled &&

View file

@ -25,11 +25,7 @@ const organizationsPathname = '/organizations';
function Organizations() {
const { getDocumentationUrl } = useDocumentationUrl();
const {
currentPlan,
currentSubscription: { planId },
currentSubscriptionQuota,
} = useContext(SubscriptionDataContext);
const { currentPlan, currentSubscriptionQuota } = useContext(SubscriptionDataContext);
const { isDevTenant } = useContext(TenantsContext);
const { navigate } = useTenantPathname();
@ -64,11 +60,8 @@ function Organizations() {
<PageMeta titleKey="organizations.page_title" />
<div className={pageLayout.headline}>
<CardTitle
paywall={
isDevFeaturesEnabled
? cond(planId === ReservedPlanId.Pro && ReservedPlanId.Pro)
: cond((isOrganizationsDisabled || isDevTenant) && ReservedPlanId.Pro)
}
paywall={cond((isOrganizationsDisabled || isDevTenant) && ReservedPlanId.Pro)}
hasAddOnTag={isDevFeaturesEnabled}
title="organizations.title"
subtitle="organizations.subtitle"
learnMoreLink={{

View file

@ -130,8 +130,9 @@ function InviteMemberModal({ isOpen, onClose }: Props) {
size="large"
title="tenant_members.invite_modal.title"
paywall={conditional(
isDevFeaturesEnabled && planId === ReservedPlanId.Pro && ReservedPlanId.Pro
isDevFeaturesEnabled && planId !== ReservedPlanId.Pro && ReservedPlanId.Pro
)}
hasAddOnTag={isDevFeaturesEnabled}
subtitle="tenant_members.invite_modal.subtitle"
footer={
conditional(