mirror of
https://github.com/logto-io/logto.git
synced 2024-12-30 20:33:54 -05:00
refactor: stop using GET /:tenantId/usage API and GET /tenants usage field (#6434)
This commit is contained in:
parent
b42b37bb72
commit
c791847879
8 changed files with 23 additions and 98 deletions
|
@ -15,9 +15,6 @@ export type LogtoSkuResponse = GetArrayElementType<GuardedResponse<GetRoutes['/a
|
||||||
|
|
||||||
export type Subscription = GuardedResponse<GetRoutes['/api/tenants/:tenantId/subscription']>;
|
export type Subscription = GuardedResponse<GetRoutes['/api/tenants/:tenantId/subscription']>;
|
||||||
|
|
||||||
/** @deprecated */
|
|
||||||
export type SubscriptionUsage = GuardedResponse<GetRoutes['/api/tenants/:tenantId/usage']>;
|
|
||||||
|
|
||||||
/* ===== Use `New` in the naming to avoid confusion with legacy types ===== */
|
/* ===== Use `New` in the naming to avoid confusion with legacy types ===== */
|
||||||
export type NewSubscriptionUsageResponse = GuardedResponse<
|
export type NewSubscriptionUsageResponse = GuardedResponse<
|
||||||
GetRoutes['/api/tenants/:tenantId/subscription-usage']
|
GetRoutes['/api/tenants/:tenantId/subscription-usage']
|
||||||
|
|
|
@ -4,10 +4,8 @@ import ReactModal from 'react-modal';
|
||||||
|
|
||||||
import PlanUsage from '@/components/PlanUsage';
|
import PlanUsage from '@/components/PlanUsage';
|
||||||
import { contactEmailLink } from '@/consts';
|
import { contactEmailLink } from '@/consts';
|
||||||
import { isDevFeaturesEnabled } from '@/consts/env';
|
|
||||||
import { subscriptionPage } from '@/consts/pages';
|
import { subscriptionPage } from '@/consts/pages';
|
||||||
import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
|
import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
|
||||||
import { TenantsContext } from '@/contexts/TenantsProvider';
|
|
||||||
import Button from '@/ds-components/Button';
|
import Button from '@/ds-components/Button';
|
||||||
import FormField from '@/ds-components/FormField';
|
import FormField from '@/ds-components/FormField';
|
||||||
import InlineNotification from '@/ds-components/InlineNotification';
|
import InlineNotification from '@/ds-components/InlineNotification';
|
||||||
|
@ -20,8 +18,6 @@ import PlanName from '../PlanName';
|
||||||
import styles from './index.module.scss';
|
import styles from './index.module.scss';
|
||||||
|
|
||||||
function MauExceededModal() {
|
function MauExceededModal() {
|
||||||
const { currentTenant } = useContext(TenantsContext);
|
|
||||||
const { usage } = currentTenant ?? {};
|
|
||||||
const {
|
const {
|
||||||
currentPlan,
|
currentPlan,
|
||||||
currentSubscription,
|
currentSubscription,
|
||||||
|
@ -38,19 +34,15 @@ function MauExceededModal() {
|
||||||
setHasClosed(true);
|
setHasClosed(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!usage || hasClosed) {
|
if (hasClosed) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const { name: planName } = currentPlan;
|
||||||
quota: { mauLimit },
|
|
||||||
name: planName,
|
|
||||||
} = currentPlan;
|
|
||||||
|
|
||||||
const isMauExceeded = isDevFeaturesEnabled
|
const isMauExceeded =
|
||||||
? currentSubscriptionQuota.mauLimit !== null &&
|
currentSubscriptionQuota.mauLimit !== null &&
|
||||||
currentSubscriptionUsage.mauLimit >= currentSubscriptionQuota.mauLimit
|
currentSubscriptionUsage.mauLimit >= currentSubscriptionQuota.mauLimit;
|
||||||
: mauLimit !== null && usage.activeUsers >= mauLimit;
|
|
||||||
|
|
||||||
if (!isMauExceeded) {
|
if (!isMauExceeded) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -93,11 +85,7 @@ function MauExceededModal() {
|
||||||
</Trans>
|
</Trans>
|
||||||
</InlineNotification>
|
</InlineNotification>
|
||||||
<FormField title="subscription.plan_usage">
|
<FormField title="subscription.plan_usage">
|
||||||
<PlanUsage
|
<PlanUsage currentSubscription={currentSubscription} currentPlan={currentPlan} />
|
||||||
subscriptionUsage={usage}
|
|
||||||
currentSubscription={currentSubscription}
|
|
||||||
currentPlan={currentPlan}
|
|
||||||
/>
|
|
||||||
</FormField>
|
</FormField>
|
||||||
</ModalLayout>
|
</ModalLayout>
|
||||||
</ReactModal>
|
</ReactModal>
|
||||||
|
|
|
@ -4,7 +4,7 @@ import classNames from 'classnames';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
|
|
||||||
import { type SubscriptionUsage, type Subscription } from '@/cloud/types/router';
|
import { type Subscription } from '@/cloud/types/router';
|
||||||
import { isDevFeaturesEnabled } from '@/consts/env';
|
import { isDevFeaturesEnabled } from '@/consts/env';
|
||||||
import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
|
import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
|
||||||
import DynamicT from '@/ds-components/DynamicT';
|
import DynamicT from '@/ds-components/DynamicT';
|
||||||
|
@ -16,15 +16,13 @@ import styles from './index.module.scss';
|
||||||
import { usageKeys, usageKeyMap, titleKeyMap, tooltipKeyMap } from './utils';
|
import { usageKeys, usageKeyMap, titleKeyMap, tooltipKeyMap } from './utils';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
/** @deprecated */
|
|
||||||
readonly subscriptionUsage: SubscriptionUsage;
|
|
||||||
/** @deprecated */
|
/** @deprecated */
|
||||||
readonly currentSubscription: Subscription;
|
readonly currentSubscription: Subscription;
|
||||||
/** @deprecated */
|
/** @deprecated */
|
||||||
readonly currentPlan: SubscriptionPlan;
|
readonly currentPlan: SubscriptionPlan;
|
||||||
};
|
};
|
||||||
|
|
||||||
function PlanUsage({ subscriptionUsage, currentSubscription, currentPlan }: Props) {
|
function PlanUsage({ currentSubscription, currentPlan }: Props) {
|
||||||
const {
|
const {
|
||||||
currentSubscriptionQuota,
|
currentSubscriptionQuota,
|
||||||
currentSubscriptionUsage,
|
currentSubscriptionUsage,
|
||||||
|
@ -35,9 +33,10 @@ function PlanUsage({ subscriptionUsage, currentSubscription, currentPlan }: Prop
|
||||||
? currentSubscriptionFromNewPricingModel
|
? currentSubscriptionFromNewPricingModel
|
||||||
: currentSubscription;
|
: currentSubscription;
|
||||||
|
|
||||||
const [activeUsers, mauLimit] = isDevFeaturesEnabled
|
const [activeUsers, mauLimit] = [
|
||||||
? [currentSubscriptionUsage.mauLimit, currentSubscriptionQuota.mauLimit]
|
currentSubscriptionUsage.mauLimit,
|
||||||
: [subscriptionUsage.activeUsers, currentPlan.quota.mauLimit];
|
isDevFeaturesEnabled ? currentSubscriptionQuota.mauLimit : currentPlan.quota.mauLimit,
|
||||||
|
];
|
||||||
|
|
||||||
const usagePercent = conditional(mauLimit && activeUsers / mauLimit);
|
const usagePercent = conditional(mauLimit && activeUsers / mauLimit);
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {
|
||||||
defaultSubscriptionQuota,
|
defaultSubscriptionQuota,
|
||||||
defaultSubscriptionUsage,
|
defaultSubscriptionUsage,
|
||||||
} from '@/consts';
|
} from '@/consts';
|
||||||
import { isCloud, isDevFeaturesEnabled } from '@/consts/env';
|
import { isCloud } from '@/consts/env';
|
||||||
import { TenantsContext } from '@/contexts/TenantsProvider';
|
import { TenantsContext } from '@/contexts/TenantsProvider';
|
||||||
import useLogtoSkus from '@/hooks/use-logto-skus';
|
import useLogtoSkus from '@/hooks/use-logto-skus';
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ const useNewSubscriptionData: () => NewSubscriptionContext & { isLoading: boolea
|
||||||
isLoading: isSubscriptionUsageDataLoading,
|
isLoading: isSubscriptionUsageDataLoading,
|
||||||
mutate: mutateSubscriptionQuotaAndUsages,
|
mutate: mutateSubscriptionQuotaAndUsages,
|
||||||
} = useSWR<NewSubscriptionUsageResponse, Error>(
|
} = useSWR<NewSubscriptionUsageResponse, Error>(
|
||||||
isCloud && isDevFeaturesEnabled && tenantId && `/api/tenants/${tenantId}/subscription-usage`,
|
isCloud && tenantId && `/api/tenants/${tenantId}/subscription-usage`,
|
||||||
async () =>
|
async () =>
|
||||||
cloudApi.get('/api/tenants/:tenantId/subscription-usage', {
|
cloudApi.get('/api/tenants/:tenantId/subscription-usage', {
|
||||||
params: { tenantId },
|
params: { tenantId },
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
import useSWR from 'swr';
|
|
||||||
|
|
||||||
import { useCloudApi } from '@/cloud/hooks/use-cloud-api';
|
|
||||||
import { type SubscriptionUsage } from '@/cloud/types/router';
|
|
||||||
import { isCloud } from '@/consts/env';
|
|
||||||
|
|
||||||
/** @deprecated */
|
|
||||||
const useSubscriptionUsage = (tenantId: string) => {
|
|
||||||
const cloudApi = useCloudApi();
|
|
||||||
|
|
||||||
return useSWR<SubscriptionUsage, Error>(isCloud && `/api/tenants/${tenantId}/usage`, async () =>
|
|
||||||
cloudApi.get('/api/tenants/:tenantId/usage', {
|
|
||||||
params: { tenantId },
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default useSubscriptionUsage;
|
|
|
@ -20,14 +20,12 @@ import {
|
||||||
} from '@/utils/subscription';
|
} from '@/utils/subscription';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
/** @deprecated No need to pass in this argument in new pricing model */
|
|
||||||
readonly activeUsers: number;
|
|
||||||
/** @deprecated No need to pass in this argument in new pricing model */
|
/** @deprecated No need to pass in this argument in new pricing model */
|
||||||
readonly currentPlan: SubscriptionPlan;
|
readonly currentPlan: SubscriptionPlan;
|
||||||
readonly className?: string;
|
readonly className?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
function MauLimitExceededNotification({ activeUsers, currentPlan, className }: Props) {
|
function MauLimitExceededNotification({ currentPlan, className }: Props) {
|
||||||
const { currentTenantId } = useContext(TenantsContext);
|
const { currentTenantId } = useContext(TenantsContext);
|
||||||
const { subscribe } = useSubscribe();
|
const { subscribe } = useSubscribe();
|
||||||
const { show } = useConfirmModal();
|
const { show } = useConfirmModal();
|
||||||
|
@ -52,7 +50,7 @@ function MauLimitExceededNotification({ activeUsers, currentPlan, className }: P
|
||||||
|
|
||||||
if (
|
if (
|
||||||
mauLimit === null || // Unlimited
|
mauLimit === null || // Unlimited
|
||||||
(isDevFeaturesEnabled ? currentSubscriptionUsage.mauLimit : activeUsers) < mauLimit ||
|
currentSubscriptionUsage.mauLimit < mauLimit ||
|
||||||
!proPlan ||
|
!proPlan ||
|
||||||
!proSku
|
!proSku
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { cond } from '@silverhand/essentials';
|
import { cond } from '@silverhand/essentials';
|
||||||
import { useContext, useMemo } from 'react';
|
import { useContext, useMemo } from 'react';
|
||||||
|
|
||||||
import { type SubscriptionUsage, type Subscription } from '@/cloud/types/router';
|
import { type Subscription } from '@/cloud/types/router';
|
||||||
import BillInfo from '@/components/BillInfo';
|
import BillInfo from '@/components/BillInfo';
|
||||||
import ChargeNotification from '@/components/ChargeNotification';
|
import ChargeNotification from '@/components/ChargeNotification';
|
||||||
import FormCard from '@/components/FormCard';
|
import FormCard from '@/components/FormCard';
|
||||||
|
@ -23,11 +23,9 @@ type Props = {
|
||||||
readonly subscription: Subscription;
|
readonly subscription: Subscription;
|
||||||
/** @deprecated */
|
/** @deprecated */
|
||||||
readonly subscriptionPlan: SubscriptionPlan;
|
readonly subscriptionPlan: SubscriptionPlan;
|
||||||
/** @deprecated */
|
|
||||||
readonly subscriptionUsage: SubscriptionUsage;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function CurrentPlan({ subscription, subscriptionPlan, subscriptionUsage }: Props) {
|
function CurrentPlan({ subscription, subscriptionPlan }: Props) {
|
||||||
const { currentSku, currentSubscription, currentSubscriptionUsage, currentSubscriptionQuota } =
|
const { currentSku, currentSubscription, currentSubscriptionUsage, currentSubscriptionQuota } =
|
||||||
useContext(SubscriptionDataContext);
|
useContext(SubscriptionDataContext);
|
||||||
const {
|
const {
|
||||||
|
@ -52,7 +50,7 @@ function CurrentPlan({ subscription, subscriptionPlan, subscriptionUsage }: Prop
|
||||||
})
|
})
|
||||||
: hasSurpassedQuotaLimit({
|
: hasSurpassedQuotaLimit({
|
||||||
quotaKey: 'tokenLimit',
|
quotaKey: 'tokenLimit',
|
||||||
usage: subscriptionUsage.tokenUsage,
|
usage: currentSubscriptionUsage.tokenLimit,
|
||||||
plan: subscriptionPlan,
|
plan: subscriptionPlan,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -67,25 +65,12 @@ function CurrentPlan({ subscription, subscriptionPlan, subscriptionUsage }: Prop
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<FormField title="subscription.plan_usage">
|
<FormField title="subscription.plan_usage">
|
||||||
<PlanUsage
|
<PlanUsage currentSubscription={subscription} currentPlan={subscriptionPlan} />
|
||||||
currentSubscription={subscription}
|
|
||||||
currentPlan={subscriptionPlan}
|
|
||||||
subscriptionUsage={subscriptionUsage}
|
|
||||||
/>
|
|
||||||
</FormField>
|
</FormField>
|
||||||
<FormField title="subscription.next_bill">
|
<FormField title="subscription.next_bill">
|
||||||
<BillInfo
|
<BillInfo cost={upcomingCost} isManagePaymentVisible={Boolean(upcomingCost)} />
|
||||||
cost={isDevFeaturesEnabled ? upcomingCost : subscriptionUsage.cost}
|
|
||||||
isManagePaymentVisible={Boolean(
|
|
||||||
isDevFeaturesEnabled ? upcomingCost : subscriptionUsage.cost
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</FormField>
|
</FormField>
|
||||||
<MauLimitExceedNotification
|
<MauLimitExceedNotification currentPlan={subscriptionPlan} className={styles.notification} />
|
||||||
activeUsers={subscriptionUsage.activeUsers}
|
|
||||||
currentPlan={subscriptionPlan}
|
|
||||||
className={styles.notification}
|
|
||||||
/>
|
|
||||||
<ChargeNotification
|
<ChargeNotification
|
||||||
hasSurpassedLimit={hasTokenSurpassedLimit}
|
hasSurpassedLimit={hasTokenSurpassedLimit}
|
||||||
quotaItemPhraseKey="tokens"
|
quotaItemPhraseKey="tokens"
|
||||||
|
|
|
@ -2,19 +2,14 @@ import { useContext } from 'react';
|
||||||
|
|
||||||
import PageMeta from '@/components/PageMeta';
|
import PageMeta from '@/components/PageMeta';
|
||||||
import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
|
import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
|
||||||
import { TenantsContext } from '@/contexts/TenantsProvider';
|
|
||||||
import useSubscriptionUsage from '@/hooks/use-subscription-usage';
|
|
||||||
import { pickupFeaturedLogtoSkus, pickupFeaturedPlans } from '@/utils/subscription';
|
import { pickupFeaturedLogtoSkus, pickupFeaturedPlans } from '@/utils/subscription';
|
||||||
|
|
||||||
import Skeleton from '../components/Skeleton';
|
|
||||||
|
|
||||||
import CurrentPlan from './CurrentPlan';
|
import CurrentPlan from './CurrentPlan';
|
||||||
import PlanComparisonTable from './PlanComparisonTable';
|
import PlanComparisonTable from './PlanComparisonTable';
|
||||||
import SwitchPlanActionBar from './SwitchPlanActionBar';
|
import SwitchPlanActionBar from './SwitchPlanActionBar';
|
||||||
import styles from './index.module.scss';
|
import styles from './index.module.scss';
|
||||||
|
|
||||||
function Subscription() {
|
function Subscription() {
|
||||||
const { currentTenantId } = useContext(TenantsContext);
|
|
||||||
const {
|
const {
|
||||||
subscriptionPlans,
|
subscriptionPlans,
|
||||||
currentPlan,
|
currentPlan,
|
||||||
|
@ -24,31 +19,13 @@ function Subscription() {
|
||||||
onCurrentSubscriptionUpdated,
|
onCurrentSubscriptionUpdated,
|
||||||
} = useContext(SubscriptionDataContext);
|
} = useContext(SubscriptionDataContext);
|
||||||
|
|
||||||
const {
|
|
||||||
data: subscriptionUsage,
|
|
||||||
isLoading,
|
|
||||||
mutate: mutateSubscriptionUsage,
|
|
||||||
} = useSubscriptionUsage(currentTenantId);
|
|
||||||
|
|
||||||
const reservedPlans = pickupFeaturedPlans(subscriptionPlans);
|
const reservedPlans = pickupFeaturedPlans(subscriptionPlans);
|
||||||
const reservedSkus = pickupFeaturedLogtoSkus(logtoSkus);
|
const reservedSkus = pickupFeaturedLogtoSkus(logtoSkus);
|
||||||
|
|
||||||
if (isLoading) {
|
|
||||||
return <Skeleton />;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!subscriptionUsage) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<PageMeta titleKey={['tenants.tabs.subscription', 'tenants.title']} />
|
<PageMeta titleKey={['tenants.tabs.subscription', 'tenants.title']} />
|
||||||
<CurrentPlan
|
<CurrentPlan subscription={currentSubscription} subscriptionPlan={currentPlan} />
|
||||||
subscription={currentSubscription}
|
|
||||||
subscriptionPlan={currentPlan}
|
|
||||||
subscriptionUsage={subscriptionUsage}
|
|
||||||
/>
|
|
||||||
<PlanComparisonTable />
|
<PlanComparisonTable />
|
||||||
<SwitchPlanActionBar
|
<SwitchPlanActionBar
|
||||||
currentSubscriptionPlanId={currentSubscription.planId}
|
currentSubscriptionPlanId={currentSubscription.planId}
|
||||||
|
@ -62,7 +39,6 @@ function Subscription() {
|
||||||
* need to manually trigger the usage update while the subscription plan is changed.
|
* need to manually trigger the usage update while the subscription plan is changed.
|
||||||
*/
|
*/
|
||||||
onCurrentSubscriptionUpdated();
|
onCurrentSubscriptionUpdated();
|
||||||
await mutateSubscriptionUsage();
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue