From 6c256f9c738fd825828bdf481cb53ed7c110d0f2 Mon Sep 17 00:00:00 2001 From: Darcy Ye Date: Fri, 2 Aug 2024 19:52:23 +0800 Subject: [PATCH] fix(console): manual update subscription data when add/delete resources (#6360) * fix(console): add post response hook to update subscription info for useApi hook * refactor: wrap sync subscription data method --- packages/console/src/hooks/use-api.ts | 26 +++++++++++--- packages/console/src/hooks/use-subscribe.ts | 35 +++++++++++++++++-- .../CreatePermissionModal/index.tsx | 7 ---- .../src/pages/ApiResourceDetails/index.tsx | 9 +---- 4 files changed, 55 insertions(+), 22 deletions(-) diff --git a/packages/console/src/hooks/use-api.ts b/packages/console/src/hooks/use-api.ts index e89d9387f..707e593fc 100644 --- a/packages/console/src/hooks/use-api.ts +++ b/packages/console/src/hooks/use-api.ts @@ -20,12 +20,13 @@ import { toast } from 'react-hot-toast'; import { useTranslation } from 'react-i18next'; import { requestTimeout } from '@/consts'; -import { isCloud } from '@/consts/env'; +import { isCloud, isDevFeaturesEnabled } from '@/consts/env'; import { AppDataContext } from '@/contexts/AppDataProvider'; import { TenantsContext } from '@/contexts/TenantsProvider'; +import { useConfirmModal } from '@/hooks/use-confirm-modal'; +import useRedirectUri from '@/hooks/use-redirect-uri'; -import { useConfirmModal } from './use-confirm-modal'; -import useRedirectUri from './use-redirect-uri'; +import useSubscribe from './use-subscribe'; export class RequestError extends Error { constructor( @@ -124,6 +125,7 @@ export const useStaticApi = ({ const toastDisabledErrorCodes = Array.isArray(hideErrorToast) ? hideErrorToast : undefined; const { handleError } = useGlobalRequestErrorHandler(toastDisabledErrorCodes); + const { syncSubscriptionData } = useSubscribe(); const api = useMemo( () => @@ -150,10 +152,25 @@ export const useStaticApi = ({ } }, ], + afterResponse: [ + async (request, _options, response) => { + if ( + isCloud && + isDevFeaturesEnabled && + ['POST', 'PUT', 'DELETE'].includes(request.method) && + response.status >= 200 && + response.status < 300 + ) { + syncSubscriptionData(); + } + }, + ], }, }), [ prefixUrl, + timeout, + signal, disableGlobalErrorHandling, handleError, isAuthenticated, @@ -161,8 +178,7 @@ export const useStaticApi = ({ getOrganizationToken, getAccessToken, i18n.language, - timeout, - signal, + syncSubscriptionData, ] ); diff --git a/packages/console/src/hooks/use-subscribe.ts b/packages/console/src/hooks/use-subscribe.ts index bf3332fab..501728262 100644 --- a/packages/console/src/hooks/use-subscribe.ts +++ b/packages/console/src/hooks/use-subscribe.ts @@ -1,7 +1,7 @@ import { ReservedPlanId } from '@logto/schemas'; import dayjs from 'dayjs'; import { nanoid } from 'nanoid'; -import { useContext, useState } from 'react'; +import { useCallback, useContext, useState } from 'react'; import { toast } from 'react-hot-toast'; import { useTranslation } from 'react-i18next'; @@ -13,6 +13,10 @@ import { GlobalRoute, TenantsContext } from '@/contexts/TenantsProvider'; import { createLocalCheckoutSession } from '@/utils/checkout'; import { dropLeadingSlash } from '@/utils/url'; +import useNewSubscriptionQuota from './use-new-subscription-quota'; +import useNewSubscriptionScopeUsage from './use-new-subscription-scopes-usage'; +import useNewSubscriptionUsage from './use-new-subscription-usage'; +import useSubscription from './use-subscription'; import useTenantPathname from './use-tenant-pathname'; type SubscribeProps = { @@ -32,10 +36,34 @@ type SubscribeProps = { const useSubscribe = () => { const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); const cloudApi = useCloudApi({ hideErrorToast: true }); - const { updateTenant } = useContext(TenantsContext); + const { updateTenant, currentTenantId } = useContext(TenantsContext); const { getUrl } = useTenantPathname(); const [isSubscribeLoading, setIsSubscribeLoading] = useState(false); + const { mutate: mutateSubscription } = useSubscription(currentTenantId); + const { mutate: mutateSubscriptionQuota } = useNewSubscriptionQuota(currentTenantId); + const { mutate: mutateSubscriptionUsage } = useNewSubscriptionUsage(currentTenantId); + const { + scopeResourceUsage: { mutate: mutateScopeResourceUsage }, + scopeRoleUsage: { mutate: mutateScopeRoleUsage }, + } = useNewSubscriptionScopeUsage(currentTenantId); + + const syncSubscriptionData = useCallback(() => { + void mutateSubscription(); + if (isDevFeaturesEnabled) { + void mutateSubscriptionQuota(); + void mutateSubscriptionUsage(); + void mutateScopeResourceUsage(); + void mutateScopeRoleUsage(); + } + }, [ + mutateScopeResourceUsage, + mutateScopeRoleUsage, + mutateSubscription, + mutateSubscriptionQuota, + mutateSubscriptionUsage, + ]); + const subscribe = async ({ skuId, planId, @@ -104,6 +132,8 @@ const useSubscribe = () => { tenantId, }, }); + + syncSubscriptionData(); updateTenant(tenantId, { planId: rest.planId, subscription: rest, @@ -147,6 +177,7 @@ const useSubscribe = () => { isSubscribeLoading, subscribe, cancelSubscription, + syncSubscriptionData, visitManagePaymentPage, }; }; 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 db712367d..ee90978e2 100644 --- a/packages/console/src/pages/ApiResourceDetails/ApiResourcePermissions/components/CreatePermissionModal/index.tsx +++ b/packages/console/src/pages/ApiResourceDetails/ApiResourcePermissions/components/CreatePermissionModal/index.tsx @@ -10,13 +10,11 @@ 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'; @@ -37,10 +35,6 @@ 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 { @@ -61,7 +55,6 @@ 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 08add872d..7dc16dcca 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, useContext } from 'react'; +import { useEffect, useState } from 'react'; import { toast } from 'react-hot-toast'; import { Trans, useTranslation } from 'react-i18next'; import { Outlet, useLocation, useParams } from 'react-router-dom'; @@ -19,13 +19,11 @@ 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'; @@ -43,10 +41,6 @@ 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}`); @@ -81,7 +75,6 @@ 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 {