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 &&
|
||||
ReservedPlanId.Pro
|
||||
)}
|
||||
hasAddOnTag={isDevFeaturesEnabled && watch('type') === ApplicationType.MachineToMachine}
|
||||
size={defaultCreateType ? 'medium' : 'large'}
|
||||
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 { 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;
|
||||
}
|
||||
|
|
|
@ -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}>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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}
|
||||
>
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 &&
|
||||
|
|
|
@ -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={{
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Reference in a new issue