diff --git a/packages/console/src/components/PlanName/index.tsx b/packages/console/src/components/PlanName/index.tsx index abd91e3a02d..45cdc98d5ff 100644 --- a/packages/console/src/components/PlanName/index.tsx +++ b/packages/console/src/components/PlanName/index.tsx @@ -2,7 +2,7 @@ import { conditional } from '@silverhand/essentials'; import { type TFuncKey } from 'i18next'; import { useTranslation } from 'react-i18next'; -import { ReservedPlanName } from '@/types/subscriptions'; +import { ReservedPlanName, ReservedSkuId } from '@/types/subscriptions'; const registeredPlanNamePhraseMap: Record< string, @@ -15,6 +15,16 @@ const registeredPlanNamePhraseMap: Record< [ReservedPlanName.Enterprise]: 'enterprise', }; +const registeredSkuIdNamePhraseMap: Record< + string, + TFuncKey<'translation', 'admin_console.subscription'> | undefined +> = { + quotaKey: undefined, + [ReservedSkuId.Free]: 'free_plan', + [ReservedSkuId.Pro]: 'pro_plan', + [ReservedSkuId.Enterprise]: 'enterprise', +}; + type Props = { /** Temporarily use optional for backward compatibility. */ readonly skuId?: string; @@ -26,7 +36,7 @@ type Props = { function PlanName({ skuId, name }: Props) { const { t } = useTranslation(undefined, { keyPrefix: 'admin_console.subscription' }); const planNamePhrase = - conditional(skuId && registeredPlanNamePhraseMap[skuId]) ?? registeredPlanNamePhraseMap[name]; + conditional(skuId && registeredSkuIdNamePhraseMap[skuId]) ?? registeredPlanNamePhraseMap[name]; /** * Note: fallback to the plan name if the phrase is not registered. diff --git a/packages/console/src/pages/ApiResourceDetails/ApiResourcePermissions/components/CreatePermissionModal/index.tsx b/packages/console/src/pages/ApiResourceDetails/ApiResourcePermissions/components/CreatePermissionModal/index.tsx index ee90978e266..db712367d89 100644 --- a/packages/console/src/pages/ApiResourceDetails/ApiResourcePermissions/components/CreatePermissionModal/index.tsx +++ b/packages/console/src/pages/ApiResourceDetails/ApiResourcePermissions/components/CreatePermissionModal/index.tsx @@ -10,11 +10,13 @@ import PlanName from '@/components/PlanName'; import QuotaGuardFooter from '@/components/QuotaGuardFooter'; import { isDevFeaturesEnabled } from '@/consts/env'; import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider'; +import { TenantsContext } from '@/contexts/TenantsProvider'; import Button from '@/ds-components/Button'; import FormField from '@/ds-components/FormField'; import ModalLayout from '@/ds-components/ModalLayout'; import TextInput from '@/ds-components/TextInput'; import useApi from '@/hooks/use-api'; +import useNewSubscriptionScopeUsage from '@/hooks/use-new-subscription-scopes-usage'; import modalStyles from '@/scss/modal.module.scss'; import { trySubmitSafe } from '@/utils/form'; import { hasReachedQuotaLimit, hasReachedSubscriptionQuotaLimit } from '@/utils/quota'; @@ -35,6 +37,10 @@ function CreatePermissionModal({ resourceId, totalResourceCount, onClose }: Prop currentSubscriptionQuota, currentSubscriptionScopeResourceUsage, } = useContext(SubscriptionDataContext); + const { currentTenantId } = useContext(TenantsContext); + const { + scopeResourceUsage: { mutate: mutateScopeResourceUsage }, + } = useNewSubscriptionScopeUsage(currentTenantId); const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); const { @@ -55,6 +61,7 @@ function CreatePermissionModal({ resourceId, totalResourceCount, onClose }: Prop .post(`api/resources/${resourceId}/scopes`, { json: formData }) .json(); + void mutateScopeResourceUsage(); onClose(createdScope); }) ); diff --git a/packages/console/src/pages/ApiResourceDetails/index.tsx b/packages/console/src/pages/ApiResourceDetails/index.tsx index 7dc16dccae5..08add872da0 100644 --- a/packages/console/src/pages/ApiResourceDetails/index.tsx +++ b/packages/console/src/pages/ApiResourceDetails/index.tsx @@ -2,7 +2,7 @@ import type { Resource } from '@logto/schemas'; import { isManagementApi, Theme } from '@logto/schemas'; import { conditionalArray } from '@silverhand/essentials'; import classNames from 'classnames'; -import { useEffect, useState } from 'react'; +import { useEffect, useState, useContext } from 'react'; import { toast } from 'react-hot-toast'; import { Trans, useTranslation } from 'react-i18next'; import { Outlet, useLocation, useParams } from 'react-router-dom'; @@ -19,11 +19,13 @@ import DetailsPageHeader, { type MenuItem } from '@/components/DetailsPage/Detai import Drawer from '@/components/Drawer'; import PageMeta from '@/components/PageMeta'; import { ApiResourceDetailsTabs } from '@/consts/page-tabs'; +import { TenantsContext } from '@/contexts/TenantsProvider'; import DeleteConfirmModal from '@/ds-components/DeleteConfirmModal'; import TabNav, { TabNavItem } from '@/ds-components/TabNav'; import type { RequestError } from '@/hooks/use-api'; import useApi from '@/hooks/use-api'; import useDocumentationUrl from '@/hooks/use-documentation-url'; +import useNewSubscriptionScopeUsage from '@/hooks/use-new-subscription-scopes-usage'; import useTenantPathname from '@/hooks/use-tenant-pathname'; import useTheme from '@/hooks/use-theme'; @@ -41,6 +43,10 @@ const icons = { function ApiResourceDetails() { const { pathname } = useLocation(); const { id, guideId } = useParams(); + const { currentTenantId } = useContext(TenantsContext); + const { + scopeResourceUsage: { mutate: mutateScopeResourceUsage }, + } = useNewSubscriptionScopeUsage(currentTenantId); const { navigate, match } = useTenantPathname(); const { getDocumentationUrl } = useDocumentationUrl(); const isGuideView = !!id && !!guideId && match(`/api-resources/${id}/guide/${guideId}`); @@ -75,6 +81,7 @@ function ApiResourceDetails() { try { await api.delete(`api/resources/${data.id}`); + void mutateScopeResourceUsage(); toast.success(t('api_resource_details.api_resource_deleted', { name: data.name })); navigate(`/api-resources`); } finally { diff --git a/packages/console/src/types/subscriptions.ts b/packages/console/src/types/subscriptions.ts index 11f832a5735..098de9ead64 100644 --- a/packages/console/src/types/subscriptions.ts +++ b/packages/console/src/types/subscriptions.ts @@ -10,6 +10,13 @@ export enum ReservedPlanName { Enterprise = 'Enterprise', } +// TODO: use `ReservedPlanId` in the future. +export enum ReservedSkuId { + Free = 'free', + Pro = 'pro', + Enterprise = 'enterprise', +} + export type SubscriptionPlanQuota = Omit< SubscriptionPlanResponse['quota'], 'builtInEmailConnectorEnabled'