0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

fix(console): fix enterprise tenant current sku always return dev (#6869)

* fix(console): fix enterprise tenant current sku always dev

fix the enterprise tenant current sku always dev bug

* fix(console): fix the lint error

fix the lint error
This commit is contained in:
simeng-li 2024-12-10 18:33:02 +08:00 committed by GitHub
parent ba84b1aed8
commit ad4800fd24
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 59 additions and 35 deletions

View file

@ -35,7 +35,7 @@ function SelectTenantPlanModal({ tenantData, onClose }: Props) {
const { subscribe } = useSubscribe();
const cloudApi = useCloudApi({ hideErrorToast: true });
const reservedBasicLogtoSkus = conditional(logtoSkus && pickupFeaturedLogtoSkus(logtoSkus));
const reservedBasicLogtoSkus = conditional(pickupFeaturedLogtoSkus(logtoSkus));
if (!reservedBasicLogtoSkus || !tenantData) {
return null;

View file

@ -1,9 +1,9 @@
import { cond, pick } from '@silverhand/essentials';
import { pick } from '@silverhand/essentials';
import { useContext, useEffect, useMemo } from 'react';
import useSWR from 'swr';
import { useCloudApi } from '@/cloud/hooks/use-cloud-api';
import { type NewSubscriptionUsageResponse } from '@/cloud/types/router';
import { type LogtoSkuResponse, type NewSubscriptionUsageResponse } from '@/cloud/types/router';
import {
defaultLogtoSku,
defaultTenantResponse,
@ -12,7 +12,8 @@ import {
} from '@/consts';
import { isCloud } from '@/consts/env';
import { TenantsContext } from '@/contexts/TenantsProvider';
import useLogtoSkus from '@/hooks/use-logto-skus';
import { LogtoSkuType } from '@/types/skus';
import { formatLogtoSkusResponses } from '@/utils/subscription';
import useSubscription from '../../hooks/use-subscription';
@ -22,7 +23,6 @@ const useNewSubscriptionData: () => NewSubscriptionContext & { isLoading: boolea
const cloudApi = useCloudApi();
const { currentTenant, currentTenantId, updateTenant } = useContext(TenantsContext);
const { isLoading: isLogtoSkusLoading, data: fetchedLogtoSkus } = useLogtoSkus();
const {
data: currentSubscription,
@ -42,7 +42,21 @@ const useNewSubscriptionData: () => NewSubscriptionContext & { isLoading: boolea
})
);
const logtoSkus = useMemo(() => cond(isCloud && fetchedLogtoSkus) ?? [], [fetchedLogtoSkus]);
// Fetch tenant specific available SKUs
// Unlike the `useLogtoSkus` hook, apart from public available SKUs, this hook also fetches tenant specific private SKUs
// For enterprise tenants who have their own private SKUs, and all grandfathered plan tenants,
// this is the only place to retrieve their current SKU data.
const { isLoading: isLogtoSkusLoading, data: fetchedLogtoSkus } = useSWR<
LogtoSkuResponse[],
Error
>(isCloud && currentTenantId && `/api/tenants/${currentTenantId}/available-skus`, async () =>
cloudApi.get('/api/tenants/:tenantId/available-skus', {
params: { tenantId: currentTenantId },
search: { type: LogtoSkuType.Basic },
})
);
const logtoSkus = useMemo(() => formatLogtoSkusResponses(fetchedLogtoSkus), [fetchedLogtoSkus]);
const currentSku = useMemo(
() => logtoSkus.find((logtoSku) => logtoSku.id === currentTenant?.planId) ?? defaultLogtoSku,

View file

@ -5,17 +5,22 @@ import useSWRImmutable from 'swr/immutable';
import { useCloudApi } from '@/cloud/hooks/use-cloud-api';
import { type LogtoSkuResponse } 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';
// eslint-disable-next-line unused-imports/no-unused-imports -- for jsDoc use
import type { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
import { LogtoSkuType } from '@/types/skus';
import { sortBy } from '@/utils/sort';
import { addSupportQuota } from '@/utils/subscription';
import { formatLogtoSkusResponses } from '@/utils/subscription';
/**
* Fetch Logto SKUs from the cloud API.
* Note: If you want to retrieve Logto SKUs under the {@link TenantAccess} component, use `SubscriptionDataContext` instead.
* Fetch public Logto SKUs from the cloud API.
*
* @remarks
* Note: This hook is used for retrieving public available Logto SKUs for all the users.
* If you want to retrieve tenant specific available Logto SKUs under the {@link TenantAccess} component,
* e.g. For enterprise tenant who have their own private SKUs, and all grandfathered plan tenants,
* use the logtoSkus from the {@link SubscriptionDataContext} instead.
*/
const useLogtoSkus = () => {
const cloudApi = useCloudApi();
@ -30,18 +35,10 @@ const useLogtoSkus = () => {
const { data: logtoSkuResponse } = useSwrResponse;
const logtoSkus: Optional<LogtoSkuResponse[]> = useMemo(() => {
if (!logtoSkuResponse) {
return;
}
return logtoSkuResponse
.map((logtoSku) => addSupportQuota(logtoSku))
.slice()
.sort(({ id: previousId }, { id: nextId }) =>
sortBy(featuredPlanIdOrder)(previousId, nextId)
);
}, [logtoSkuResponse]);
const logtoSkus: Optional<LogtoSkuResponse[]> = useMemo(
() => formatLogtoSkusResponses(logtoSkuResponse),
[logtoSkuResponse]
);
return {
...useSwrResponse,

View file

@ -14,7 +14,6 @@ import SkuName from '@/components/SkuName';
import { checkoutStateQueryKey } from '@/consts/subscriptions';
import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
import { TenantsContext } from '@/contexts/TenantsProvider';
import useLogtoSkus from '@/hooks/use-logto-skus';
import useTenantPathname from '@/hooks/use-tenant-pathname';
import { clearLocalCheckoutSession, getLocalCheckoutSession } from '@/utils/checkout';
@ -32,9 +31,6 @@ function CheckoutSuccessCallback() {
const checkoutState = new URLSearchParams(search).get(checkoutStateQueryKey);
const { state, sessionId, callbackPage, isDowngrade } = getLocalCheckoutSession() ?? {};
const { data: logtoSkus, error: fetchLogtoSkusError } = useLogtoSkus();
const isLoadingLogtoSkus = !logtoSkus && !fetchLogtoSkusError;
// Note: if we can't get the subscription results in 10 seconds, we will redirect to the console home page
useTimer({
autoStart: true,
@ -63,7 +59,6 @@ function CheckoutSuccessCallback() {
);
const checkoutTenantId = stripeCheckoutSession?.tenantId;
const checkoutPlanId = stripeCheckoutSession?.planId;
const checkoutSkuId = stripeCheckoutSession?.skuId;
const { data: tenantSubscription } = useSWR(
@ -80,19 +75,18 @@ function CheckoutSuccessCallback() {
const isCheckoutSuccessful =
checkoutTenantId &&
stripeCheckoutSession.status === 'complete' &&
!isLoadingLogtoSkus &&
checkoutSkuId === tenantSubscription?.planId;
useEffect(() => {
if (isCheckoutSuccessful) {
clearLocalCheckoutSession();
const checkoutSku = logtoSkus?.find((sku) => sku.id === checkoutPlanId);
if (checkoutSku) {
// Make the typescript happy checkoutSkuId should not be empty here
if (checkoutSkuId) {
toast.success(
<Trans
components={{
name: <SkuName skuId={checkoutSku.id} />,
name: <SkuName skuId={checkoutSkuId} />,
}}
>
{t(isDowngrade ? 'downgrade_success' : 'upgrade_success')}
@ -124,12 +118,11 @@ function CheckoutSuccessCallback() {
}
}, [
callbackPage,
checkoutPlanId,
checkoutSkuId,
checkoutTenantId,
currentTenantId,
isCheckoutSuccessful,
isDowngrade,
logtoSkus,
navigate,
navigateTenant,
onCurrentSubscriptionUpdated,

View file

@ -9,7 +9,7 @@ import { ticketSupportResponseTimeMap } from '@/consts/plan-quotas';
import { featuredPlanIdOrder, featuredPlanIds } from '@/consts/subscriptions';
import { type LogtoSkuQuota } from '@/types/skus';
export const addSupportQuota = (logtoSkuResponse: LogtoSkuResponse) => {
const addSupportQuota = (logtoSkuResponse: LogtoSkuResponse) => {
const { id, quota } = logtoSkuResponse;
return {
@ -24,6 +24,26 @@ export const addSupportQuota = (logtoSkuResponse: LogtoSkuResponse) => {
};
};
/**
* Format Logto SKUs responses.
*
* - add support quota to the SKUs.
* - Sort the SKUs by the order of `featuredPlanIdOrder`.
*/
export const formatLogtoSkusResponses = (logtoSkus: LogtoSkuResponse[] | undefined) => {
if (!logtoSkus) {
return [];
}
return logtoSkus
.map((logtoSku) => addSupportQuota(logtoSku))
.slice()
.sort(
({ id: previousId }, { id: nextId }) =>
featuredPlanIdOrder.indexOf(previousId) - featuredPlanIdOrder.indexOf(nextId)
);
};
const getSubscriptionPlanOrderById = (id: string) => {
const index = featuredPlanIdOrder.indexOf(id);