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:
parent
ba84b1aed8
commit
ad4800fd24
5 changed files with 59 additions and 35 deletions
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue