diff --git a/packages/console/src/cloud/types/router.ts b/packages/console/src/cloud/types/router.ts index 88f6c6d5d..c96b32347 100644 --- a/packages/console/src/cloud/types/router.ts +++ b/packages/console/src/cloud/types/router.ts @@ -7,10 +7,6 @@ type GetTenantAuthRoutes = RouterRoutes['get']; export type GetArrayElementType = T extends Array ? U : never; -export type SubscriptionPlanResponse = GuardedResponse< - GetRoutes['/api/subscription-plans'] ->[number]; - export type LogtoSkuResponse = GetArrayElementType>; export type Subscription = GuardedResponse; diff --git a/packages/console/src/consts/tenants.ts b/packages/console/src/consts/tenants.ts index 4dc1a0f0d..40408b0be 100644 --- a/packages/console/src/consts/tenants.ts +++ b/packages/console/src/consts/tenants.ts @@ -9,7 +9,6 @@ import { } from '@/cloud/types/router'; import { RegionName } from '@/components/Region'; import { LogtoSkuType } from '@/types/skus'; -import { type SubscriptionPlan } from '@/types/subscriptions'; import { adminEndpoint, isCloud } from './env'; @@ -48,44 +47,6 @@ export const defaultTenantResponse: TenantResponse = { createdAt: new Date(), }; -/** - * - For cloud, the initial tenant's subscription plan will be fetched from the cloud API. - * - OSS has a fixed subscription plan with `development` id and no cloud API to dynamically fetch the subscription plan. - */ -export const defaultSubscriptionPlan: SubscriptionPlan = { - id: defaultSubscriptionPlanId, - name: 'Development', - createdAt: new Date(), - updatedAt: new Date(), - stripeProducts: [], - quota: { - mauLimit: null, - tokenLimit: null, - applicationsLimit: null, - machineToMachineLimit: null, - resourcesLimit: null, - scopesPerResourceLimit: null, - customDomainEnabled: true, - mfaEnabled: true, - omniSignInEnabled: true, - socialConnectorsLimit: null, - standardConnectorsLimit: null, - rolesLimit: null, - machineToMachineRolesLimit: null, - scopesPerRoleLimit: null, - auditLogsRetentionDays: null, - hooksLimit: null, - organizationsEnabled: true, - ssoEnabled: true, - ticketSupportResponseTime: 48, - thirdPartyApplicationsLimit: null, - tenantMembersLimit: null, - customJwtEnabled: true, - subjectTokenEnabled: true, - bringYourUiEnabled: true, - }, -}; - /** * - For cloud, the initial tenant's subscription plan will be fetched from the cloud API. * - OSS has a fixed subscription plan with `development` id and no cloud API to dynamically fetch the subscription plan. diff --git a/packages/console/src/contexts/SubscriptionDataProvider/index.tsx b/packages/console/src/contexts/SubscriptionDataProvider/index.tsx index 0f3d239d3..d0536c729 100644 --- a/packages/console/src/contexts/SubscriptionDataProvider/index.tsx +++ b/packages/console/src/contexts/SubscriptionDataProvider/index.tsx @@ -2,7 +2,6 @@ import { noop } from '@silverhand/essentials'; import { createContext, type ReactNode } from 'react'; import { - defaultSubscriptionPlan, defaultLogtoSku, defaultTenantResponse, defaultSubscriptionQuota, @@ -21,8 +20,6 @@ const defaultSubscription = defaultTenantResponse.subscription; * CAUTION: You should only use this data context under the {@link TenantAccess} component */ export const SubscriptionDataContext = createContext({ - subscriptionPlans: [], - currentPlan: defaultSubscriptionPlan, currentSubscription: defaultSubscription, onCurrentSubscriptionUpdated: noop, /* ==== For new pricing model ==== */ diff --git a/packages/console/src/contexts/SubscriptionDataProvider/types.ts b/packages/console/src/contexts/SubscriptionDataProvider/types.ts index 314143de3..ccb427449 100644 --- a/packages/console/src/contexts/SubscriptionDataProvider/types.ts +++ b/packages/console/src/contexts/SubscriptionDataProvider/types.ts @@ -6,13 +6,8 @@ import { type NewSubscriptionResourceScopeUsage, type NewSubscriptionRoleScopeUsage, } from '@/cloud/types/router'; -import { type SubscriptionPlan } from '@/types/subscriptions'; export type Context = { - /** @deprecated */ - subscriptionPlans: SubscriptionPlan[]; - /** @deprecated */ - currentPlan: SubscriptionPlan; currentSubscription: Subscription; onCurrentSubscriptionUpdated: (subscription?: Subscription) => void; }; @@ -28,7 +23,6 @@ type NewSubscriptionSupplementContext = { mutateSubscriptionQuotaAndUsages: () => void; }; -export type NewSubscriptionContext = Omit & - NewSubscriptionSupplementContext; +export type NewSubscriptionContext = Context & NewSubscriptionSupplementContext; export type FullContext = Context & NewSubscriptionSupplementContext; diff --git a/packages/console/src/contexts/SubscriptionDataProvider/use-subscription-data.ts b/packages/console/src/contexts/SubscriptionDataProvider/use-subscription-data.ts index 5051e0b27..b59f48e96 100644 --- a/packages/console/src/contexts/SubscriptionDataProvider/use-subscription-data.ts +++ b/packages/console/src/contexts/SubscriptionDataProvider/use-subscription-data.ts @@ -1,37 +1,23 @@ -import { cond, condString } from '@silverhand/essentials'; -import { useContext, useMemo } from 'react'; +import { condString } from '@silverhand/essentials'; +import { useContext } from 'react'; -import { defaultSubscriptionPlan, defaultTenantResponse } from '@/consts'; -import { isCloud } from '@/consts/env'; +import { defaultTenantResponse } from '@/consts'; import { TenantsContext } from '@/contexts/TenantsProvider'; import useSubscription from '../../hooks/use-subscription'; -import useSubscriptionPlans from '../../hooks/use-subscription-plans'; import { type Context } from './types'; const useSubscriptionData: () => Context & { isLoading: boolean } = () => { const { currentTenant } = useContext(TenantsContext); - const { isLoading: isSubscriptionPlansLoading, data: fetchedPlans } = useSubscriptionPlans(); const { data: currentSubscription, isLoading: isSubscriptionLoading, mutate: mutateSubscription, } = useSubscription(condString(currentTenant?.id)); - const subscriptionPlans = useMemo(() => cond(isCloud && fetchedPlans) ?? [], [fetchedPlans]); - - const currentPlan = useMemo( - () => - subscriptionPlans.find((plan) => plan.id === currentTenant?.planId) ?? - defaultSubscriptionPlan, - [currentTenant?.planId, subscriptionPlans] - ); - return { - isLoading: isSubscriptionLoading || isSubscriptionPlansLoading, - subscriptionPlans, - currentPlan, + isLoading: isSubscriptionLoading, currentSubscription: currentSubscription ?? defaultTenantResponse.subscription, onCurrentSubscriptionUpdated: mutateSubscription, }; diff --git a/packages/console/src/hooks/use-subscription-plans.ts b/packages/console/src/hooks/use-subscription-plans.ts deleted file mode 100644 index 1ae69bd16..000000000 --- a/packages/console/src/hooks/use-subscription-plans.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { type Optional } from '@silverhand/essentials'; -import { useMemo } from 'react'; -import useSWRImmutable from 'swr/immutable'; - -import { useCloudApi } from '@/cloud/hooks/use-cloud-api'; -import { type SubscriptionPlanResponse } from '@/cloud/types/router'; -import { isCloud } from '@/consts/env'; -import { featuredPlanIdOrder } from '@/consts/subscriptions'; -// Used in the docs -// eslint-disable-next-line unused-imports/no-unused-imports -import TenantAccess from '@/containers/TenantAccess'; -import { type SubscriptionPlan } from '@/types/subscriptions'; -import { sortBy } from '@/utils/sort'; -import { addSupportQuotaToPlan } from '@/utils/subscription'; - -/** - * @deprecated - * Fetch subscription plans from the cloud API. - * Note: If you want to retrieve subscription plans under the {@link TenantAccess} component, use `SubscriptionDataContext` instead. - */ -const useSubscriptionPlans = () => { - const cloudApi = useCloudApi(); - - const useSwrResponse = useSWRImmutable( - isCloud && '/api/subscription-plans', - async () => cloudApi.get('/api/subscription-plans') - ); - - const { data: subscriptionPlansResponse } = useSwrResponse; - - const subscriptionPlans: Optional = useMemo(() => { - if (!subscriptionPlansResponse) { - return; - } - - return subscriptionPlansResponse - .map((plan) => addSupportQuotaToPlan(plan)) - .slice() - .sort(({ id: previousId }, { id: nextId }) => - sortBy(featuredPlanIdOrder)(previousId, nextId) - ); - }, [subscriptionPlansResponse]); - - return { - ...useSwrResponse, - data: subscriptionPlans, - }; -}; - -export default useSubscriptionPlans; diff --git a/packages/console/src/pages/CheckoutSuccessCallback/index.tsx b/packages/console/src/pages/CheckoutSuccessCallback/index.tsx index 99460c4ef..f3f3c2c55 100644 --- a/packages/console/src/pages/CheckoutSuccessCallback/index.tsx +++ b/packages/console/src/pages/CheckoutSuccessCallback/index.tsx @@ -15,7 +15,6 @@ import { checkoutStateQueryKey } from '@/consts/subscriptions'; import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider'; import { TenantsContext } from '@/contexts/TenantsProvider'; import useLogtoSkus from '@/hooks/use-logto-skus'; -import useSubscriptionPlans from '@/hooks/use-subscription-plans'; import useTenantPathname from '@/hooks/use-tenant-pathname'; import { clearLocalCheckoutSession, getLocalCheckoutSession } from '@/utils/checkout'; @@ -33,8 +32,6 @@ function CheckoutSuccessCallback() { const checkoutState = new URLSearchParams(search).get(checkoutStateQueryKey); const { state, sessionId, callbackPage, isDowngrade } = getLocalCheckoutSession() ?? {}; - const { data: subscriptionPlans, error: fetchPlansError } = useSubscriptionPlans(); - const isLoadingPlans = !subscriptionPlans && !fetchPlansError; const { data: logtoSkus, error: fetchLogtoSkusError } = useLogtoSkus(); const isLoadingLogtoSkus = !logtoSkus && !fetchLogtoSkusError; @@ -132,12 +129,11 @@ function CheckoutSuccessCallback() { navigate, navigateTenant, onCurrentSubscriptionUpdated, - subscriptionPlans, t, tenantSubscription, ]); - if (!isValidSession && !isLoadingPlans) { + if (!isValidSession) { return ; } diff --git a/packages/console/src/pages/TenantSettings/Subscription/CurrentPlan/MauLimitExceededNotification/index.tsx b/packages/console/src/pages/TenantSettings/Subscription/CurrentPlan/MauLimitExceededNotification/index.tsx index 8d45dbe46..7e0d5f6a4 100644 --- a/packages/console/src/pages/TenantSettings/Subscription/CurrentPlan/MauLimitExceededNotification/index.tsx +++ b/packages/console/src/pages/TenantSettings/Subscription/CurrentPlan/MauLimitExceededNotification/index.tsx @@ -23,15 +23,10 @@ function MauLimitExceededNotification({ periodicUsage: rawPeriodicUsage, classNa const { currentTenantId } = useContext(TenantsContext); const { subscribe } = useSubscribe(); const { show } = useConfirmModal(); - const { subscriptionPlans, logtoSkus, currentSubscriptionQuota } = - useContext(SubscriptionDataContext); + const { logtoSkus, currentSubscriptionQuota } = useContext(SubscriptionDataContext); const { currentTenant } = useContext(TenantsContext); const [isLoading, setIsLoading] = useState(false); - const proPlan = useMemo( - () => subscriptionPlans.find(({ id }) => id === ReservedPlanId.Pro), - [subscriptionPlans] - ); const proSku = useMemo(() => logtoSkus.find(({ id }) => id === ReservedPlanId.Pro), [logtoSkus]); const periodicUsage = useMemo( @@ -55,7 +50,6 @@ function MauLimitExceededNotification({ periodicUsage: rawPeriodicUsage, classNa if ( mauLimit === null || // Unlimited periodicUsage.mauLimit < mauLimit || - !proPlan || !proSku ) { return null; @@ -72,7 +66,7 @@ function MauLimitExceededNotification({ periodicUsage: rawPeriodicUsage, classNa setIsLoading(true); await subscribe({ skuId: proSku.id, - planId: proPlan.id, + planId: proSku.id, tenantId: currentTenantId, callbackPage: subscriptionPage, }); diff --git a/packages/console/src/types/subscriptions.ts b/packages/console/src/types/subscriptions.ts index 2d4b370ba..b28e07fe9 100644 --- a/packages/console/src/types/subscriptions.ts +++ b/packages/console/src/types/subscriptions.ts @@ -1,6 +1,6 @@ import { z } from 'zod'; -import { type InvoicesResponse, type SubscriptionPlanResponse } from '@/cloud/types/router'; +import { type InvoicesResponse } from '@/cloud/types/router'; export enum ReservedPlanName { Free = 'Free', @@ -19,18 +19,6 @@ export enum ReservedSkuId { Enterprise = 'enterprise', } -type SubscriptionPlanQuota = Omit< - SubscriptionPlanResponse['quota'], - 'builtInEmailConnectorEnabled' -> & { - // Add ticket support quota item to the plan since it will be compared in the downgrade plan notification modal. - ticketSupportResponseTime: number; -}; - -export type SubscriptionPlan = Omit & { - quota: SubscriptionPlanQuota; -}; - export const localCheckoutSessionGuard = z.object({ state: z.string(), sessionId: z.string(), diff --git a/packages/console/src/utils/subscription.ts b/packages/console/src/utils/subscription.ts index 84f54d882..4d689b8b6 100644 --- a/packages/console/src/utils/subscription.ts +++ b/packages/console/src/utils/subscription.ts @@ -4,26 +4,11 @@ import { ResponseError } from '@withtyped/client'; import dayjs from 'dayjs'; import { tryReadResponseErrorBody } from '@/cloud/hooks/use-cloud-api'; -import { type LogtoSkuResponse, type SubscriptionPlanResponse } from '@/cloud/types/router'; +import { type LogtoSkuResponse } from '@/cloud/types/router'; import { ticketSupportResponseTimeMap } from '@/consts/plan-quotas'; import { featuredPlanIdOrder, featuredPlanIds } from '@/consts/subscriptions'; import { type LogtoSkuQuota } from '@/types/skus'; -export const addSupportQuotaToPlan = (subscriptionPlanResponse: SubscriptionPlanResponse) => { - const { id, quota } = subscriptionPlanResponse; - - return { - ...subscriptionPlanResponse, - quota: { - ...quota, - /** - * Manually add this support quota item to the plan since it will be compared in the downgrade plan notification modal. - */ - ticketSupportResponseTime: ticketSupportResponseTimeMap[id] ?? 0, // Fallback to not supported - }, - }; -}; - export const addSupportQuota = (logtoSkuResponse: LogtoSkuResponse) => { const { id, quota } = logtoSkuResponse;