1+ import { type Region as RegionType } from '@logto/cloud/routes' ;
12import { Theme , TenantTag } from '@logto/schemas' ;
2- import { useState } from 'react' ;
3+ import { useMemo , useState } from 'react' ;
34import { Controller , FormProvider , useForm } from 'react-hook-form' ;
45import { toast } from 'react-hot-toast' ;
56import { useTranslation } from 'react-i18next' ;
67import Modal from 'react-modal' ;
8+ import useSWRImmutable from 'swr/immutable' ;
79
810import CreateTenantHeaderIconDark from '@/assets/icons/create-tenant-header-dark.svg?react' ;
911import CreateTenantHeaderIcon from '@/assets/icons/create-tenant-header.svg?react' ;
1012import { useCloudApi } from '@/cloud/hooks/use-cloud-api' ;
1113import { type TenantResponse } from '@/cloud/types/router' ;
1214import Region , { defaultRegionName } from '@/components/Region' ;
13- import { availableRegions } from '@/consts' ;
1415import Button from '@/ds-components/Button' ;
1516import DangerousRaw from '@/ds-components/DangerousRaw' ;
1617import FormField from '@/ds-components/FormField' ;
1718import ModalLayout from '@/ds-components/ModalLayout' ;
1819import RadioGroup , { Radio } from '@/ds-components/RadioGroup' ;
20+ import { Ring } from '@/ds-components/Spinner' ;
1921import TextInput from '@/ds-components/TextInput' ;
2022import useTheme from '@/hooks/use-theme' ;
2123import modalStyles from '@/scss/modal.module.scss' ;
@@ -31,11 +33,19 @@ type Props = {
3133 readonly onClose : ( tenant ?: TenantResponse ) => void ;
3234} ;
3335
34- const availableTags = [ TenantTag . Development , TenantTag . Production ] ;
36+ const allTags = Object . freeze ( [ TenantTag . Development , TenantTag . Production ] ) ;
3537
3638function CreateTenantModal ( { isOpen, onClose } : Props ) {
3739 const [ tenantData , setTenantData ] = useState < CreateTenantData > ( ) ;
3840 const theme = useTheme ( ) ;
41+ const cloudApi = useCloudApi ( ) ;
42+ const { data : regions , error : regionsError } = useSWRImmutable < RegionType [ ] , Error > (
43+ 'api/me/regions' ,
44+ async ( ) => {
45+ const { regions } = await cloudApi . get ( '/api/me/regions' ) ;
46+ return regions ;
47+ }
48+ ) ;
3949
4050 const defaultValues = Object . freeze ( {
4151 tag : TenantTag . Development ,
@@ -51,10 +61,14 @@ function CreateTenantModal({ isOpen, onClose }: Props) {
5161 handleSubmit,
5262 formState : { errors, isSubmitting } ,
5363 register,
64+ watch,
5465 } = methods ;
5566
56- const cloudApi = useCloudApi ( ) ;
57-
67+ const regionName = watch ( 'regionName' ) ;
68+ const currentRegion = useMemo (
69+ ( ) => regions ?. find ( ( region ) => region . id === regionName ) ,
70+ [ regions , regionName ]
71+ ) ;
5872 const createTenant = async ( { name, tag, regionName } : CreateTenantData ) => {
5973 const newTenant = await cloudApi . post ( '/api/tenants' , { body : { name, tag, regionName } } ) ;
6074 onClose ( newTenant ) ;
@@ -118,54 +132,61 @@ function CreateTenantModal({ isOpen, onClose }: Props) {
118132 disabled = { isSubmitting }
119133 />
120134 </ FormField >
135+
121136 < FormField
122137 title = "tenants.settings.tenant_region"
123138 tip = { t ( 'tenants.settings.tenant_region_description' ) }
124139 >
125- < Controller
126- control = { control }
127- name = "regionName"
128- rules = { { required : true } }
129- render = { ( { field : { onChange, value, name } } ) => (
130- < RadioGroup type = "small" name = { name } value = { value } onChange = { onChange } >
131- { availableRegions . map ( ( region ) => (
132- < Radio
133- key = { region }
134- title = {
135- < DangerousRaw >
136- < Region regionName = { region } />
137- </ DangerousRaw >
138- }
139- value = { region }
140- isDisabled = { isSubmitting }
141- />
142- ) ) }
143- </ RadioGroup >
144- ) }
145- />
146- </ FormField >
147- < FormField title = "tenants.create_modal.tenant_usage_purpose" >
148- < Controller
149- control = { control }
150- name = "tag"
151- rules = { { required : true } }
152- render = { ( { field : { onChange, value, name } } ) => (
153- < RadioGroup
154- type = "card"
155- className = { styles . envTagRadioGroup }
156- value = { value }
157- name = { name }
158- onChange = { onChange }
159- >
160- { availableTags . map ( ( tag ) => (
161- < Radio key = { tag } value = { tag } >
162- < EnvTagOptionContent tag = { tag } />
163- </ Radio >
164- ) ) }
165- </ RadioGroup >
166- ) }
167- />
140+ { ! regions && ! regionsError && < Ring /> }
141+ { regionsError && < span className = { styles . error } > { regionsError . message } </ span > }
142+ { regions && ! regionsError && (
143+ < Controller
144+ control = { control }
145+ name = "regionName"
146+ rules = { { required : true } }
147+ render = { ( { field : { onChange, value, name } } ) => (
148+ < RadioGroup type = "plain" name = { name } value = { value } onChange = { onChange } >
149+ { regions . map ( ( region ) => (
150+ < Radio
151+ key = { region . id }
152+ title = {
153+ < DangerousRaw >
154+ < Region region = { region } />
155+ </ DangerousRaw >
156+ }
157+ value = { region . id }
158+ isDisabled = { isSubmitting }
159+ />
160+ ) ) }
161+ </ RadioGroup >
162+ ) }
163+ />
164+ ) }
168165 </ FormField >
166+ { currentRegion && (
167+ < FormField title = "tenants.create_modal.tenant_usage_purpose" >
168+ < Controller
169+ control = { control }
170+ name = "tag"
171+ rules = { { required : true } }
172+ render = { ( { field : { onChange, value, name } } ) => (
173+ < RadioGroup
174+ type = "card"
175+ className = { styles . envTagRadioGroup }
176+ value = { value }
177+ name = { name }
178+ onChange = { onChange }
179+ >
180+ { currentRegion . tags . map ( ( tag ) => (
181+ < Radio key = { tag } value = { tag } >
182+ < EnvTagOptionContent tag = { tag } />
183+ </ Radio >
184+ ) ) }
185+ </ RadioGroup >
186+ ) }
187+ />
188+ </ FormField >
189+ ) }
169190 </ FormProvider >
170191 < SelectTenantPlanModal
171192 tenantData = { tenantData }
0 commit comments