Skip to content

Commit e5724ef

Browse files
authored
refactor(console): refactor third-party and saml app paywall tag (#7713)
* refactor(console): refactor third-party and saml app paywall tag refactor third-party and daml app paywall tag * fix(console): fix third-party paywall tag display logic fix third-party paywall tag display logic
1 parent 0c35176 commit e5724ef

File tree

5 files changed

+58
-50
lines changed

5 files changed

+58
-50
lines changed

packages/console/src/assets/docs/guides/saml-idp/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { type GuideMetadata } from '../types';
44

55
const metadata = Object.freeze({
66
name: 'SAML',
7-
description: 'Use Logto as a SAML identity provider (IdP) for your application.',
7+
description: 'XML-based framework for SSO. Use if apps only support SAML.',
88
target: ApplicationType.SAML,
99
isThirdParty: false,
1010
skipGuideAfterCreation: true,

packages/console/src/assets/docs/guides/third-party-oidc/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { type GuideMetadata } from '../types';
44

55
const metadata: Readonly<GuideMetadata> = Object.freeze({
66
name: 'OIDC',
7-
description: 'Use Logto as a third-party OIDC identity provider (IdP) for your application.',
7+
description: 'Use Logto as a third-party OAuth / OIDC identity provider to offer user consent.',
88
target: ApplicationType.Traditional,
99
isThirdParty: true,
1010
skipGuideAfterCreation: true,

packages/console/src/components/Guide/GuideCard/index.tsx

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import { ApplicationType, Theme } from '@logto/schemas';
1+
import { Theme } from '@logto/schemas';
22
import classNames from 'classnames';
3-
import { Suspense, useCallback, useContext } from 'react';
3+
import { type ReactNode, Suspense, useCallback, useContext } from 'react';
44

55
import { type Guide, type GuideMetadata } from '@/assets/docs/guides/types';
6-
import FeatureTag, { BetaTag } from '@/components/FeatureTag';
7-
import { latestProPlanId } from '@/consts/subscriptions';
6+
import { BetaTag } from '@/components/FeatureTag';
87
import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
98
import Button from '@/ds-components/Button';
109
import useTheme from '@/hooks/use-theme';
@@ -22,11 +21,11 @@ type Props = {
2221
readonly onClick: (data: SelectedGuide) => void;
2322
readonly hasBorder?: boolean;
2423
readonly hasButton?: boolean;
25-
readonly hasPaywall?: boolean;
24+
readonly paywallTag?: ReactNode;
2625
readonly isBeta?: boolean;
2726
};
2827

29-
function GuideCard({ data, onClick, hasBorder, hasButton, hasPaywall, isBeta }: Props) {
28+
function GuideCard({ data, onClick, hasBorder, hasButton, paywallTag, isBeta }: Props) {
3029
const { id, Logo, DarkLogo, metadata } = data;
3130
const {
3231
currentSubscription: { isEnterprisePlan },
@@ -35,7 +34,7 @@ function GuideCard({ data, onClick, hasBorder, hasButton, hasPaywall, isBeta }:
3534
const { target, name, description } = metadata;
3635
const buttonText = target === 'API' ? 'guide.get_started' : 'guide.start_building';
3736
const theme = useTheme();
38-
const hasTags = Boolean(hasPaywall) || Boolean(isBeta);
37+
const hasTags = Boolean(paywallTag) || Boolean(isBeta);
3938

4039
const handleClick = useCallback(() => {
4140
onClick({ id, metadata });
@@ -66,12 +65,7 @@ function GuideCard({ data, onClick, hasBorder, hasButton, hasPaywall, isBeta }:
6665
<div className={styles.name}>{name}</div>
6766
{hasTags && (
6867
<div className={styles.tagWrapper}>
69-
{hasPaywall &&
70-
(target === ApplicationType.SAML ? (
71-
<FeatureTag isEnterprise isVisible={!isEnterprisePlan} />
72-
) : (
73-
<FeatureTag isVisible plan={latestProPlanId} />
74-
))}
68+
{paywallTag}
7569
{isBeta && <BetaTag />}
7670
</div>
7771
)}

packages/console/src/components/Guide/GuideCardGroup/index.tsx

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
import { ApplicationType } from '@logto/schemas';
2+
import { conditional } from '@silverhand/essentials';
23
import classNames from 'classnames';
34
import { type Ref, forwardRef, useContext } from 'react';
45

5-
import { type Guide } from '@/assets/docs/guides/types';
6-
import { isCloud } from '@/consts/env';
6+
import { type GuideMetadata, type Guide } from '@/assets/docs/guides/types';
7+
import { type NewSubscriptionQuota } from '@/cloud/types/router';
8+
import FeatureTag, { CombinedAddOnAndFeatureTag } from '@/components/FeatureTag';
9+
import { isDevFeaturesEnabled } from '@/consts/env';
10+
import { latestProPlanId } from '@/consts/subscriptions';
711
import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
8-
import { TenantsContext } from '@/contexts/TenantsProvider';
12+
import { isPaidPlan } from '@/utils/subscription';
913

1014
import GuideCard, { type SelectedGuide } from '../GuideCard';
1115

@@ -20,12 +24,46 @@ type Props = {
2024
readonly onClickGuide: (data: SelectedGuide) => void;
2125
};
2226

27+
function getPaywallTag(
28+
guideMetadata: GuideMetadata,
29+
currentSubscriptionQuota: NewSubscriptionQuota,
30+
isPaidPlan: boolean,
31+
isEnterprisePlan: boolean
32+
) {
33+
// TODO: remove the dev feature guard when the new add-on plan is ready for release
34+
if (guideMetadata.isThirdParty) {
35+
return isDevFeaturesEnabled ? (
36+
<CombinedAddOnAndFeatureTag
37+
hasAddOnTag={isPaidPlan && currentSubscriptionQuota.thirdPartyApplicationsLimit !== null}
38+
paywall={conditional(!isPaidPlan && latestProPlanId)}
39+
/>
40+
) : (
41+
<FeatureTag isVisible={!isPaidPlan} plan={latestProPlanId} />
42+
);
43+
}
44+
45+
if (guideMetadata.target === ApplicationType.SAML) {
46+
return isDevFeaturesEnabled ? (
47+
<CombinedAddOnAndFeatureTag
48+
hasAddOnTag={isPaidPlan}
49+
paywall={conditional(!isPaidPlan && latestProPlanId)}
50+
/>
51+
) : (
52+
<FeatureTag isEnterprise isVisible={!isEnterprisePlan} />
53+
);
54+
}
55+
}
56+
2357
function GuideCardGroup(
2458
{ className, categoryName, guides, hasCardBorder, hasCardButton, onClickGuide }: Props,
2559
ref: Ref<HTMLDivElement>
2660
) {
27-
const { isDevTenant } = useContext(TenantsContext);
28-
const { currentSubscriptionQuota } = useContext(SubscriptionDataContext);
61+
const {
62+
currentSubscriptionQuota,
63+
currentSubscription: { planId, isEnterprisePlan },
64+
} = useContext(SubscriptionDataContext);
65+
66+
const isPaidTenant = isPaidPlan(planId, isEnterprisePlan);
2967

3068
if (!guides?.length) {
3169
return null;
@@ -41,14 +79,12 @@ function GuideCardGroup(
4179
hasBorder={hasCardBorder}
4280
hasButton={hasCardButton}
4381
data={guide}
44-
hasPaywall={
45-
isCloud &&
46-
((guide.metadata.isThirdParty &&
47-
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
48-
(currentSubscriptionQuota.thirdPartyApplicationsLimit === 0 || isDevTenant)) ||
49-
(guide.metadata.target === ApplicationType.SAML &&
50-
(currentSubscriptionQuota.samlApplicationsLimit === 0 || isDevTenant)))
51-
}
82+
paywallTag={getPaywallTag(
83+
guide.metadata,
84+
currentSubscriptionQuota,
85+
isPaidTenant,
86+
isEnterprisePlan
87+
)}
5288
onClick={onClickGuide}
5389
/>
5490
))}

packages/console/src/pages/Applications/components/GuideLibrary/index.tsx

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
1-
import { cond } from '@silverhand/essentials';
21
import classNames from 'classnames';
32
import { useCallback, useContext, useMemo, useState } from 'react';
43
import { useTranslation } from 'react-i18next';
54
import { useLocation } from 'react-router-dom';
65

76
import SearchIcon from '@/assets/icons/search.svg?react';
87
import EmptyDataPlaceholder from '@/components/EmptyDataPlaceholder';
9-
import FeatureTag from '@/components/FeatureTag';
108
import { type SelectedGuide } from '@/components/Guide/GuideCard';
119
import GuideCardGroup from '@/components/Guide/GuideCardGroup';
1210
import { useAppGuideMetadata } from '@/components/Guide/hooks';
1311
import { isCloud } from '@/consts/env';
14-
import { latestProPlanId } from '@/consts/subscriptions';
1512
import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
1613
import { CheckboxGroup } from '@/ds-components/Checkbox';
1714
import OverlayScrollbar from '@/ds-components/OverlayScrollbar';
@@ -100,25 +97,6 @@ function GuideLibrary({ className, hasCardBorder, hasCardButton, onSelectGuide }
10097
.map((category) => ({
10198
title: `guide.categories.${category}`,
10299
value: category,
103-
...cond(
104-
isCloud &&
105-
category === 'ThirdParty' && {
106-
tag: (
107-
<FeatureTag
108-
isVisible={
109-
currentSubscriptionQuota.thirdPartyApplicationsLimit === 0
110-
}
111-
plan={latestProPlanId}
112-
/>
113-
),
114-
}
115-
),
116-
...cond(
117-
isCloud &&
118-
category === 'SAML' && {
119-
tag: <FeatureTag isEnterprise isVisible={!isEnterprisePlan} />,
120-
}
121-
),
122100
}))}
123101
value={filterCategories}
124102
onChange={(value) => {

0 commit comments

Comments
 (0)