Skip to content

Commit c3c64c7

Browse files
authored
feat(core,console): add paywall for hide watermark (#7917)
1 parent 3dba50f commit c3c64c7

File tree

24 files changed

+137
-4
lines changed

24 files changed

+137
-4
lines changed

packages/console/src/pages/SignInExperience/PageContent/Branding/BrandingForm/index.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { generateDarkColor } from '@logto/core-kit';
22
import { Theme } from '@logto/schemas';
3-
import { useMemo, useCallback, useEffect } from 'react';
3+
import { useMemo, useCallback, useEffect, useContext } from 'react';
44
import { Controller, useFormContext } from 'react-hook-form';
55
import { useTranslation } from 'react-i18next';
66

77
import LogoAndFavicon from '@/components/ImageInputs/LogoAndFavicon';
88
import { isCloud, isDevFeaturesEnabled } from '@/consts/env';
9+
import { latestProPlanId } from '@/consts/subscriptions';
10+
import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
911
import Button from '@/ds-components/Button';
1012
import Card from '@/ds-components/Card';
1113
import ColorPicker from '@/ds-components/ColorPicker';
@@ -26,6 +28,8 @@ function BrandingForm() {
2628
control,
2729
formState: { errors, isDirty },
2830
} = useFormContext<SignInExperienceForm>();
31+
const { currentSubscriptionQuota } = useContext(SubscriptionDataContext);
32+
const isHideLogtoBrandingEnabled = currentSubscriptionQuota.bringYourUiEnabled;
2933

3034
const isDarkModeEnabled = watch('color.isDarkModeEnabled');
3135
const primaryColor = watch('color.primaryColor');
@@ -117,10 +121,17 @@ function BrandingForm() {
117121
</>
118122
)}
119123
{showHideLogtoBranding && (
120-
<FormField title="sign_in_exp.branding.hide_logto_branding">
124+
<FormField
125+
title="sign_in_exp.branding.hide_logto_branding"
126+
featureTag={{
127+
isVisible: !isHideLogtoBrandingEnabled,
128+
plan: latestProPlanId,
129+
}}
130+
>
121131
<Switch
122132
label={t('sign_in_exp.branding.hide_logto_branding_description')}
123133
{...register('hideLogtoBranding')}
134+
disabled={!isHideLogtoBrandingEnabled}
124135
/>
125136
</FormField>
126137
)}

packages/console/src/pages/SignInExperience/PageContent/Branding/CustomUiForm/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Trans, useTranslation } from 'react-i18next';
44

55
import CustomCssEditorField from '@/components/CustomCssEditorField';
66
import InlineUpsell from '@/components/InlineUpsell';
7-
import { isCloud } from '@/consts/env';
7+
import { isCloud, isDevFeaturesEnabled } from '@/consts/env';
88
import { latestProPlanId } from '@/consts/subscriptions';
99
import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
1010
import Card from '@/ds-components/Card';
@@ -63,7 +63,7 @@ function CustomUiForm() {
6363
/>
6464
)}
6565
/>
66-
{!isBringYourUiEnabled && (
66+
{!isBringYourUiEnabled && !isDevFeaturesEnabled && (
6767
<InlineUpsell
6868
className={brandingStyles.upsell}
6969
for="bring_your_ui"
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
@use '@/scss/underscore' as _;
2+
3+
.inlineNotification {
4+
padding: _.unit(3) _.unit(4);
5+
font: var(--font-body-2);
6+
display: flex;
7+
border-radius: 8px;
8+
align-items: center;
9+
gap: _.unit(3);
10+
margin-bottom: _.unit(4);
11+
12+
&.shadow {
13+
border: 1px solid var(--color-border);
14+
box-shadow: var(--shadow-1);
15+
}
16+
17+
.icon {
18+
flex-shrink: 0;
19+
width: 20px;
20+
height: 20px;
21+
}
22+
23+
.content {
24+
flex: 1;
25+
overflow: hidden;
26+
overflow-wrap: break-word;
27+
28+
// Cancel the margin from MDX generated paragraphs
29+
&:has(> p) {
30+
margin: _.unit(-4) 0;
31+
}
32+
}
33+
34+
&.info {
35+
background: var(--color-info-container);
36+
37+
.icon {
38+
color: var(--color-on-info-container);
39+
}
40+
}
41+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import classNames from 'classnames';
2+
import { useContext } from 'react';
3+
import { useTranslation } from 'react-i18next';
4+
5+
import { isCloud, isDevFeaturesEnabled } from '@/consts/env';
6+
import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
7+
import TextLink from '@/ds-components/TextLink';
8+
import useTenantPathname from '@/hooks/use-tenant-pathname';
9+
10+
import styles from './index.module.scss';
11+
12+
function UpsellNotice() {
13+
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
14+
const { navigate } = useTenantPathname();
15+
const { currentSubscriptionQuota } = useContext(SubscriptionDataContext);
16+
const isBringYourUiEnabled = currentSubscriptionQuota.bringYourUiEnabled;
17+
const showHideLogtoBranding = isCloud && isDevFeaturesEnabled;
18+
19+
if (!showHideLogtoBranding || isBringYourUiEnabled) {
20+
return null;
21+
}
22+
23+
return (
24+
<div className={classNames(styles.inlineNotification, styles.info, styles.plain)}>
25+
<div className={styles.content}>{t('upsell.paywall.branding_customization')}</div>
26+
<div className={styles.action}>
27+
<TextLink
28+
onClick={() => {
29+
navigate('/tenant-settings/subscription');
30+
}}
31+
>
32+
{t('upsell.view_plans')}
33+
</TextLink>
34+
</div>
35+
</div>
36+
);
37+
}
38+
39+
export default UpsellNotice;

packages/console/src/pages/SignInExperience/PageContent/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434

3535
import AccountCenter from './AccountCenter';
3636
import Branding from './Branding';
37+
import UpsellNotice from './Branding/UpsellNotice';
3738
import CollectUserProfile from './CollectUserProfile';
3839
import Content from './Content';
3940
import SignUpAndSignIn from './SignUpAndSignIn';
@@ -219,6 +220,7 @@ function PageContent({ data, onSignInExperienceUpdated, onAccountCenterUpdated }
219220
</PageTab>
220221
</TabNav>
221222
<div className={styles.content}>
223+
{tab === SignInExperienceTab.Branding && <UpsellNotice />}
222224
<div className={classNames(styles.contentTop, isDirty && styles.withSubmitActionBar)}>
223225
<FormProvider {...methods}>
224226
<form>

packages/core/src/queries/tenant-usage/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ export default class TenantUsageQuery {
252252
select exists (
253253
select * from ${signInExperiencesTable}
254254
where ${signInExperiencesFields.customUiAssets} is not null
255+
or ${signInExperiencesFields.hideLogtoBranding} = true
255256
)
256257
`,
257258
sql`bringYourUiEnabled`,

packages/core/src/routes/sign-in-experience/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,11 @@ export default function signInExperiencesRoutes<T extends ManagementApiRouter>(
188188
);
189189
}
190190

191+
// Guard the quota for BYUI if the hideLogtoBranding is set to true
192+
if (hideLogtoBranding) {
193+
await quota.guardTenantUsageByKey('bringYourUiEnabled');
194+
}
195+
191196
const payload = {
192197
...rest,
193198
...conditional(

packages/phrases/src/locales/ar/translation/admin-console/upsell/paywall.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ const paywall = {
6666
description:
6767
'قم بالترقية إلى خطة مدفوعة للحصول على وظيفة JWT المخصصة والفوائد المتميزة. لا تتردد في <a>الاتصال بنا</a> إذا كان لديك أي أسئلة.',
6868
},
69+
branding_customization:
70+
'افتح التحكم الكامل في الهوية البصرية باستخدام ميزتي "إخفاء علامة Logto" و"أحضر واجهتك" عن طريق ترقية خطتك.',
6971
bring_your_ui:
7072
'قم بالترقية إلى خطة مدفوعة للحصول على وظيفة إحضار واجهة المستخدم المخصصة والفوائد المتميزة.',
7173
security_features:

packages/phrases/src/locales/de/translation/admin-console/upsell/paywall.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ const paywall = {
6666
description:
6767
'Upgrade auf einen kostenpflichtigen Plan für benutzerdefinierte JWT-Funktionalität und Premium-Vorteile. Wenn Sie Fragen haben, zögern Sie nicht, uns zu <a>kontaktieren</a>.',
6868
},
69+
branding_customization:
70+
'Schalten Sie die vollständige Branding-Kontrolle mit den Funktionen "Logto-Branding ausblenden" und "Bring your UI" frei, indem Sie Ihren Plan upgraden.',
6971
bring_your_ui:
7072
'Upgrade auf einen kostenpflichtigen Plan für benutzerdefinierte UI-Funktionalität und Premium-Vorteile.',
7173
security_features:

packages/phrases/src/locales/en/translation/admin-console/upsell/paywall.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ const paywall = {
6666
description:
6767
"Upgrade to a paid plan for custom JWT functionality and premium benefits. Don't hesitate to <a>contact us</a> if you have any questions.",
6868
},
69+
branding_customization:
70+
'Unlock complete branding control with "Hide Logto branding" and "Bring your UI" features by upgrading your plan.',
6971
bring_your_ui:
7072
'Upgrade to a paid plan for bring your custom UI functionality and premium benefits.',
7173
security_features:

0 commit comments

Comments
 (0)