From 3c51ecc29d6a974129f8c01497055100dcbd6629 Mon Sep 17 00:00:00 2001 From: Xiao Yijun Date: Wed, 19 Jul 2023 13:56:00 +0800 Subject: [PATCH] feat(console): apply quota limit for custom domain (#4181) --- .../src/components/FormCard/index.module.scss | 2 + .../console/src/components/FormCard/index.tsx | 4 +- .../src/components/ProTag/index.module.scss | 11 ++++ .../console/src/components/ProTag/index.tsx | 13 +++++ .../hooks/use-current-subscription-plan.ts | 21 ++++++++ .../AddDomainForm/index.tsx | 6 ++- .../TenantDomainSettings/index.module.scss | 4 ++ .../TenantDomainSettings/index.tsx | 52 +++++++++++++++++-- 8 files changed, 107 insertions(+), 6 deletions(-) create mode 100644 packages/console/src/components/ProTag/index.module.scss create mode 100644 packages/console/src/components/ProTag/index.tsx create mode 100644 packages/console/src/hooks/use-current-subscription-plan.ts diff --git a/packages/console/src/components/FormCard/index.module.scss b/packages/console/src/components/FormCard/index.module.scss index 998d9e576..7cbc48c35 100644 --- a/packages/console/src/components/FormCard/index.module.scss +++ b/packages/console/src/components/FormCard/index.module.scss @@ -3,6 +3,8 @@ .title { @include _.section-head-1; color: var(--color-neutral-variant-60); + display: flex; + align-items: center; } .description { diff --git a/packages/console/src/components/FormCard/index.tsx b/packages/console/src/components/FormCard/index.tsx index 338343946..6e50da46b 100644 --- a/packages/console/src/components/FormCard/index.tsx +++ b/packages/console/src/components/FormCard/index.tsx @@ -10,12 +10,13 @@ import * as styles from './index.module.scss'; type Props = { title: AdminConsoleKey; + tag?: ReactNode; description?: AdminConsoleKey; learnMoreLink?: string; children: ReactNode; }; -function FormCard({ title, description, learnMoreLink, children }: Props) { +function FormCard({ title, tag, description, learnMoreLink, children }: Props) { const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); return ( @@ -24,6 +25,7 @@ function FormCard({ title, description, learnMoreLink, children }: Props) { <>
+ {tag}
{description && (
diff --git a/packages/console/src/components/ProTag/index.module.scss b/packages/console/src/components/ProTag/index.module.scss new file mode 100644 index 000000000..e34694132 --- /dev/null +++ b/packages/console/src/components/ProTag/index.module.scss @@ -0,0 +1,11 @@ +@use '@/scss/underscore' as _; + +.tag { + @include _.section-head-2; + display: inline-block; + background-color: var(--color-primary); + border-radius: 10px; + padding: 0 _.unit(1); + color: var(--color-white); + margin: 0 _.unit(1); +} diff --git a/packages/console/src/components/ProTag/index.tsx b/packages/console/src/components/ProTag/index.tsx new file mode 100644 index 000000000..e118492e8 --- /dev/null +++ b/packages/console/src/components/ProTag/index.tsx @@ -0,0 +1,13 @@ +import DynamicT from '@/ds-components/DynamicT'; + +import * as styles from './index.module.scss'; + +function ProTag() { + return ( +
+ +
+ ); +} + +export default ProTag; diff --git a/packages/console/src/hooks/use-current-subscription-plan.ts b/packages/console/src/hooks/use-current-subscription-plan.ts new file mode 100644 index 000000000..3e6971695 --- /dev/null +++ b/packages/console/src/hooks/use-current-subscription-plan.ts @@ -0,0 +1,21 @@ +import { useMemo } from 'react'; + +import useCurrentSubscription from './use-current-subscription'; +import useSubscriptionPlans from './use-subscription-plans'; + +const useCurrentSubscriptionPlan = () => { + const { data: subscription, error: fetchSubscriptionError } = useCurrentSubscription(); + const { data: subscriptionPlans, error: fetchSubscriptionPlansError } = useSubscriptionPlans(); + + const currentPlan = useMemo( + () => subscriptionPlans?.find(({ id: planId }) => planId === subscription?.planId), + [subscription?.planId, subscriptionPlans] + ); + + return { + data: currentPlan, + error: fetchSubscriptionError ?? fetchSubscriptionPlansError, + }; +}; + +export default useCurrentSubscriptionPlan; diff --git a/packages/console/src/pages/TenantSettings/TenantDomainSettings/AddDomainForm/index.tsx b/packages/console/src/pages/TenantSettings/TenantDomainSettings/AddDomainForm/index.tsx index 423a19bf4..2aa3f58eb 100644 --- a/packages/console/src/pages/TenantSettings/TenantDomainSettings/AddDomainForm/index.tsx +++ b/packages/console/src/pages/TenantSettings/TenantDomainSettings/AddDomainForm/index.tsx @@ -17,10 +17,11 @@ type FormData = { }; type Props = { + isCustomDomainEnabled: boolean; onCustomDomainAdded: (domain: Domain) => void; }; -function AddDomainForm({ onCustomDomainAdded }: Props) { +function AddDomainForm({ isCustomDomainEnabled, onCustomDomainAdded }: Props) { const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); const { register, @@ -47,6 +48,7 @@ function AddDomainForm({ onCustomDomainAdded }: Props) { return (
diff --git a/packages/console/src/pages/TenantSettings/TenantDomainSettings/index.module.scss b/packages/console/src/pages/TenantSettings/TenantDomainSettings/index.module.scss index 94d210aa1..2bc40a6d2 100644 --- a/packages/console/src/pages/TenantSettings/TenantDomainSettings/index.module.scss +++ b/packages/console/src/pages/TenantSettings/TenantDomainSettings/index.module.scss @@ -5,3 +5,7 @@ margin-top: _.unit(4); } } + +.upsellNotification { + margin-top: _.unit(4); +} diff --git a/packages/console/src/pages/TenantSettings/TenantDomainSettings/index.tsx b/packages/console/src/pages/TenantSettings/TenantDomainSettings/index.tsx index e73057138..c650e7705 100644 --- a/packages/console/src/pages/TenantSettings/TenantDomainSettings/index.tsx +++ b/packages/console/src/pages/TenantSettings/TenantDomainSettings/index.tsx @@ -1,8 +1,16 @@ import { withAppInsights } from '@logto/app-insights/react'; +import { Trans, useTranslation } from 'react-i18next'; +import { useNavigate } from 'react-router-dom'; import FormCard from '@/components/FormCard'; import PageMeta from '@/components/PageMeta'; +import ProTag from '@/components/ProTag'; +import { contactEmailLink } from '@/consts'; +import { isProduction } from '@/consts/env'; import FormField from '@/ds-components/FormField'; +import InlineNotification from '@/ds-components/InlineNotification'; +import TextLink from '@/ds-components/TextLink'; +import useCurrentSubscriptionPlan from '@/hooks/use-current-subscription-plan'; import useCustomDomain from '@/hooks/use-custom-domain'; import useDocumentationUrl from '@/hooks/use-documentation-url'; @@ -14,18 +22,34 @@ import DefaultDomain from './DefaultDomain'; import * as styles from './index.module.scss'; function TenantDomainSettings() { - const { data: customDomain, isLoading, mutate } = useCustomDomain(true); + const { data: customDomain, isLoading: isLoadingCustomDomain, mutate } = useCustomDomain(true); + const { data: currentPlan, error: fetchCurrentPlanError } = useCurrentSubscriptionPlan(); + const isLoadingCurrentPlan = !currentPlan && !fetchCurrentPlanError; const { getDocumentationUrl } = useDocumentationUrl(); + const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); + const navigate = useNavigate(); - if (isLoading) { + if (isLoadingCustomDomain || isLoadingCurrentPlan) { return ; } + const customDomainEnabled = + /** + * Todo: @xiaoyijun remove this condition on subscription features ready. + */ + isProduction || + Boolean(currentPlan?.quota.customDomainEnabled) || + /** + * Note: this is for tenants which already have a custom domain before we have subscription features. + */ + Boolean(customDomain); + return (
} description="domain.custom.custom_domain_description" learnMoreLink={getDocumentationUrl('docs/recipes/custom-domain')} > @@ -33,7 +57,29 @@ function TenantDomainSettings() { {customDomain ? ( ) : ( - + + )} + {!isProduction && !customDomain && !customDomainEnabled && ( + { + navigate('/tenant-settings/subscription'); + }} + > + , + }} + > + {t('upsell.paywall.custom_domain')} + + )}