Skip to content

Commit fbad1dd

Browse files
authored
fix: fix console UI related issues (#7915)
1 parent bb495ef commit fbad1dd

File tree

3 files changed

+72
-48
lines changed

3 files changed

+72
-48
lines changed

packages/console/src/components/CreateTenantModal/EnvTagOptionContent/index.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ import styles from './index.module.scss';
1111

1212
type Props = {
1313
readonly tag: TenantTag;
14+
/**
15+
* Whether to hide the available production plan information.
16+
* Set to true when the available production plans should not be displayed,
17+
* for example, in contexts where this information is not relevant or should be hidden from the user.
18+
*/
19+
readonly isAvailableProductionPlanInvisible: boolean;
1420
};
1521

1622
const descriptionMap: Record<TenantTag, AdminConsoleKey> = {
@@ -20,19 +26,19 @@ const descriptionMap: Record<TenantTag, AdminConsoleKey> = {
2026

2127
const availableProductionPlanNames = [ReservedPlanName.Free, ReservedPlanName.Pro];
2228

23-
function EnvTagOptionContent({ tag }: Props) {
29+
function EnvTagOptionContent({ tag, isAvailableProductionPlanInvisible = false }: Props) {
2430
return (
2531
<div className={styles.container}>
2632
<TenantEnvTag isAbbreviated={false} tag={tag} size="large" className={styles.tag} />
2733
<div className={styles.description}>
2834
<DynamicT forKey={descriptionMap[tag]} />
2935
</div>
30-
<Divider />
36+
{(tag === TenantTag.Development || !isAvailableProductionPlanInvisible) && <Divider />}
3137
<div className={styles.hint}>
3238
{tag === TenantTag.Development && (
3339
<DynamicT forKey="tenants.create_modal.development_hint" />
3440
)}
35-
{tag === TenantTag.Production && (
41+
{tag === TenantTag.Production && !isAvailableProductionPlanInvisible && (
3642
<>
3743
<DynamicT forKey="tenants.create_modal.available_plan" />
3844
{availableProductionPlanNames.map((planName) => (

packages/console/src/components/CreateTenantModal/index.tsx

Lines changed: 62 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import { type Region as RegionType } from '@logto/cloud/routes';
12
import { Theme, TenantTag } from '@logto/schemas';
3+
import { condArray } from '@silverhand/essentials';
24
import { useCallback, useMemo, useState } from 'react';
35
import { Controller, FormProvider, useForm } from 'react-hook-form';
46
import { toast } from 'react-hot-toast';
@@ -11,8 +13,8 @@ import { useCloudApi } from '@/cloud/hooks/use-cloud-api';
1113
import { type TenantResponse } from '@/cloud/types/router';
1214
import Region, {
1315
defaultRegionName,
14-
getInstanceDropdownItems,
1516
logtoDropdownItem,
17+
type InstanceDropdownItemProps,
1618
} from '@/components/Region';
1719
import { isDevFeaturesEnabled } from '@/consts/env';
1820
import Button from '@/ds-components/Button';
@@ -38,6 +40,19 @@ type Props = {
3840
readonly onClose: (tenant?: TenantResponse) => void;
3941
};
4042

43+
const checkPrivateRegionAccess = (regions: RegionType[]): boolean => {
44+
return regions.some(({ isPrivate }) => isPrivate);
45+
};
46+
47+
const getInstanceDropdownItems = (regions: RegionType[]): InstanceDropdownItemProps[] => {
48+
const hasPublicRegions = regions.some(({ isPrivate }) => !isPrivate);
49+
const privateInstances = regions
50+
.filter(({ isPrivate }) => isPrivate)
51+
.map(({ id, name, country, tags }) => ({ id, name, country, tags }));
52+
53+
return condArray(hasPublicRegions && logtoDropdownItem, ...privateInstances);
54+
};
55+
4156
// eslint-disable-next-line complexity
4257
function CreateTenantModal({ isOpen, onClose }: Props) {
4358
const [tenantData, setTenantData] = useState<CreateTenantData>();
@@ -73,6 +88,7 @@ function CreateTenantModal({ isOpen, onClose }: Props) {
7388
);
7489

7590
const instanceOptions = useMemo(() => getInstanceDropdownItems(regions ?? []), [regions]);
91+
const hasPrivateRegionsAccess = useMemo(() => checkPrivateRegionAccess(regions ?? []), [regions]);
7692

7793
const publicRegions = useMemo(
7894
() => regions?.filter((region) => !region.isPrivate) ?? [],
@@ -83,7 +99,7 @@ function CreateTenantModal({ isOpen, onClose }: Props) {
8399

84100
const currentRegion = useMemo(() => {
85101
if (isDevFeaturesEnabled) {
86-
return getRegionById(regionName);
102+
return getRegionById(isLogtoInstance ? regionName : instanceId);
87103
}
88104

89105
if (isLogtoInstance) {
@@ -93,14 +109,20 @@ function CreateTenantModal({ isOpen, onClose }: Props) {
93109
return regions?.find((region) => region.id === instanceId);
94110
}, [isLogtoInstance, regionName, instanceId, getRegionById, regions]);
95111

112+
const getFinalRegionName = useCallback(
113+
(instanceId: string, regionName: string) => {
114+
if (isDevFeaturesEnabled) {
115+
return isLogtoInstance ? regionName : instanceId;
116+
}
117+
return regionName;
118+
},
119+
[isLogtoInstance]
120+
);
121+
96122
const createTenant = async ({ name, tag, instanceId, regionName }: CreateTenantData) => {
97123
// For Logto public instance, use the selected region
98124
// For private instances, use the instance ID as the region
99-
const finalRegionName = isDevFeaturesEnabled
100-
? isLogtoInstance
101-
? regionName
102-
: instanceId
103-
: regionName;
125+
const finalRegionName = getFinalRegionName(instanceId, regionName);
104126
const newTenant = await cloudApi.post('/api/tenants', {
105127
body: { name, tag, regionName: finalRegionName },
106128
});
@@ -117,7 +139,8 @@ function CreateTenantModal({ isOpen, onClose }: Props) {
117139
return;
118140
}
119141

120-
setTenantData(data);
142+
// For production tenants, store creation parameters with the correct regionName for later use after plan selection.
143+
setTenantData({ ...data, regionName: getFinalRegionName(data.instanceId, data.regionName) });
121144
})
122145
);
123146

@@ -199,32 +222,8 @@ function CreateTenantModal({ isOpen, onClose }: Props) {
199222
</FormField>
200223
)}
201224

202-
{currentRegion && (
203-
<FormField title="tenants.create_modal.tenant_usage_purpose">
204-
<Controller
205-
control={control}
206-
name="tag"
207-
rules={{ required: true }}
208-
render={({ field: { onChange, value, name } }) => (
209-
<RadioGroup
210-
type="card"
211-
className={styles.envTagRadioGroup}
212-
value={value}
213-
name={name}
214-
onChange={onChange}
215-
>
216-
{currentRegion.tags.map((tag) => (
217-
<Radio key={tag} value={tag}>
218-
<EnvTagOptionContent tag={tag} />
219-
</Radio>
220-
))}
221-
</RadioGroup>
222-
)}
223-
/>
224-
</FormField>
225-
)}
226-
227-
{isDevFeaturesEnabled && (
225+
{/* Only show the instance selector (dropdown) if there are private regions available. */}
226+
{isDevFeaturesEnabled && hasPrivateRegionsAccess && (
228227
<FormField
229228
title="tenants.settings.tenant_instance"
230229
tip={t('tenants.settings.tenant_instance_description')}
@@ -281,6 +280,35 @@ function CreateTenantModal({ isOpen, onClose }: Props) {
281280
)}
282281
</FormField>
283282
)}
283+
284+
{currentRegion && (
285+
<FormField title="tenants.create_modal.tenant_usage_purpose">
286+
<Controller
287+
control={control}
288+
name="tag"
289+
rules={{ required: true }}
290+
render={({ field: { onChange, value, name } }) => (
291+
<RadioGroup
292+
type="card"
293+
className={styles.envTagRadioGroup}
294+
value={value}
295+
name={name}
296+
onChange={onChange}
297+
>
298+
{currentRegion.tags.map((tag) => (
299+
<Radio key={tag} value={tag}>
300+
{/* If the region is private (for enterprise customers), we hide the available production plan. */}
301+
<EnvTagOptionContent
302+
tag={tag}
303+
isAvailableProductionPlanInvisible={currentRegion.isPrivate}
304+
/>
305+
</Radio>
306+
))}
307+
</RadioGroup>
308+
)}
309+
/>
310+
</FormField>
311+
)}
284312
</FormProvider>
285313
<SelectTenantPlanModal
286314
tenantData={tenantData}

packages/console/src/components/Region/index.tsx

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { type PublicRegionName, type Region as RegionType } from '@logto/cloud/routes';
22
import { TenantTag } from '@logto/schemas';
3-
import { condArray } from '@silverhand/essentials';
43
import classNames from 'classnames';
54
import { useMemo, type FunctionComponent } from 'react';
65
import { useTranslation } from 'react-i18next';
@@ -82,7 +81,7 @@ export function StaticRegion({ isComingSoon = false, regionName, className }: St
8281
);
8382
}
8483

85-
type InstanceDropdownItemProps = Pick<RegionType, 'id' | 'name' | 'country' | 'tags'>;
84+
export type InstanceDropdownItemProps = Pick<RegionType, 'id' | 'name' | 'country' | 'tags'>;
8685

8786
export const logtoDropdownItem: InstanceDropdownItemProps = {
8887
id: 'logto',
@@ -91,15 +90,6 @@ export const logtoDropdownItem: InstanceDropdownItemProps = {
9190
tags: Object.values(TenantTag),
9291
};
9392

94-
export const getInstanceDropdownItems = (regions: RegionType[]): InstanceDropdownItemProps[] => {
95-
const hasPublicRegions = regions.some(({ isPrivate }) => !isPrivate);
96-
const privateInstances = regions
97-
.filter(({ isPrivate }) => isPrivate)
98-
.map(({ id, name, country, tags }) => ({ id, name, country, tags }));
99-
100-
return condArray(hasPublicRegions && logtoDropdownItem, ...privateInstances);
101-
};
102-
10393
type Props = {
10494
readonly region: RegionType;
10595
readonly className?: string;

0 commit comments

Comments
 (0)