1
- // environmentList.tsx - Main listing page for environments
2
- import React , { useState } from "react" ;
1
+ import React , { useEffect , useState } from "react" ;
3
2
import { ADMIN_ROLE , SUPER_ADMIN_ROLE } from "constants/orgConstants" ;
4
3
import { AddIcon , CustomModal , DangerIcon , EditPopover } from "lowcoder-design" ;
5
4
import { useDispatch , useSelector } from "react-redux" ;
6
- // Replace these with actual actions when available
7
- // import { createEnvironmentAction, deleteEnvironmentAction, updateEnvironmentApiKeyAction } from "redux/reduxActions/environmentActions";
5
+ import {
6
+ fetchEnvironments ,
7
+ deleteEnvironmentAction ,
8
+ updateEnvironmentApiKeyAction ,
9
+ } from "redux/reduxActions/enterpriseActions" ;
8
10
import styled from "styled-components" ;
9
11
import { trans , transToNode } from "i18n" ;
10
12
import { buildEnvironmentId } from "constants/routesURL" ;
@@ -16,7 +18,10 @@ import {
16
18
} from "../permission/styledComponents" ;
17
19
import { Table } from "components/Table" ;
18
20
import history from "util/history" ;
19
- import { Level1SettingPageContentWithList , Level1SettingPageTitleWithBtn } from "../styled" ;
21
+ import {
22
+ Level1SettingPageContentWithList ,
23
+ Level1SettingPageTitleWithBtn ,
24
+ } from "../styled" ;
20
25
import { timestampToHumanReadable } from "util/dateTimeUtils" ;
21
26
import { isSaasMode } from "util/envUtils" ;
22
27
import { selectSystemConfig } from "redux/selectors/configSelectors" ;
@@ -25,8 +30,12 @@ import { default as Input } from "antd/es/input";
25
30
import { default as Modal } from "antd/es/modal" ;
26
31
import { default as Tooltip } from "antd/es/tooltip" ;
27
32
import { getUser } from "redux/selectors/usersSelectors" ;
28
- // Replace with actual selector when available
29
- // import { getEnvironmentCreateStatus } from "redux/selectors/environmentSelectors";
33
+ import {
34
+ selectEnvironments ,
35
+ selectEnvironmentsLoading ,
36
+ selectEnvironmentsError ,
37
+ selectApiKeyUpdating ,
38
+ } from "redux/selectors/enterpriseSelectors" ;
30
39
import { StyledTag } from "./styledComponents" ;
31
40
32
41
const EnvironmentName = styled . div `
@@ -42,7 +51,7 @@ const EnvironmentName = styled.div`
42
51
43
52
const DomainName = styled . div `
44
53
font-size: 13px;
45
- color: #8B8FA3 ;
54
+ color: #8b8fa3 ;
46
55
` ;
47
56
48
57
const TableStyled = styled ( Table ) `
@@ -119,14 +128,22 @@ const ApiKeyStatusIcon = styled.div`
119
128
display: inline-flex;
120
129
align-items: center;
121
130
margin-left: 8px;
122
-
131
+
123
132
svg {
124
133
width: 16px;
125
134
height: 16px;
126
- color: ${ props => props . color || "#8B8FA3" } ;
135
+ color: ${ ( props ) => props . color || "#8B8FA3" } ;
127
136
}
128
137
` ;
129
138
139
+
140
+ const LoadingWrapper = styled . div `
141
+ display: flex;
142
+ justify-content: center;
143
+ align-items: center;
144
+ height: 200px;
145
+ ` ;
146
+
130
147
type DataItemInfo = {
131
148
id : string ;
132
149
name : string ;
@@ -139,57 +156,36 @@ type DataItemInfo = {
139
156
} ;
140
157
141
158
function EnvironmentSetting ( ) {
142
- // For now, use mock data until we have Redux actions and selectors for environments
143
- const mockEnvironments = [
144
- {
145
- id : "env1" ,
146
- name : "Development" ,
147
- domain : "lowcoder-dev.company.com" ,
148
- stage : "development" ,
149
- isMaster : true ,
150
- hasApiKey : true ,
151
- createTime : new Date ( Date . now ( ) - 7 * 24 * 60 * 60 * 1000 ) . toISOString ( ) ,
152
- } ,
153
- {
154
- id : "env2" ,
155
- name : "Testing" ,
156
- domain : "lowcoder-test.company.com" ,
157
- stage : "testing" ,
158
- isMaster : false ,
159
- hasApiKey : true ,
160
- createTime : new Date ( Date . now ( ) - 7 * 24 * 60 * 60 * 1000 ) . toISOString ( ) ,
161
- } ,
162
- {
163
- id : "env3" ,
164
- name : "Production" ,
165
- domain : "lowcoder-prod.company.com" ,
166
- stage : "production" ,
167
- isMaster : false ,
168
- hasApiKey : false ,
169
- createTime : new Date ( Date . now ( ) - 7 * 24 * 60 * 60 * 1000 ) . toISOString ( ) ,
170
- } ,
171
- ] ;
172
-
173
159
const user = useSelector ( getUser ) ;
174
- const environments = mockEnvironments ; // Replace with actual environments from Redux when available
175
160
const dispatch = useDispatch ( ) ;
176
161
const sysConfig = useSelector ( selectSystemConfig ) ;
162
+
163
+ // Get environment data from Redux
164
+ const environments = useSelector ( selectEnvironments ) ;
165
+ const loading = useSelector ( selectEnvironmentsLoading ) ;
166
+ const error = useSelector ( selectEnvironmentsError ) ;
167
+ const apiKeyUpdating = useSelector ( selectApiKeyUpdating ) ;
168
+
177
169
const [ form ] = Form . useForm ( ) ;
178
170
const [ apiKeyForm ] = Form . useForm ( ) ;
179
171
const [ isApiKeyModalVisible , setIsApiKeyModalVisible ] = useState ( false ) ;
180
- const [ currentEnvironment , setCurrentEnvironment ] = useState < DataItemInfo | null > ( null ) ;
172
+ const [ currentEnvironment , setCurrentEnvironment ] =
173
+ useState < DataItemInfo | null > ( null ) ;
181
174
182
- // Mock state for environment creation (replace with actual selector when available)
183
- const environmentCreateStatus = "idle" ; // useSelector(getEnvironmentCreateStatus);
175
+ // Fetch environments when component mounts
176
+ useEffect ( ( ) => {
177
+ dispatch ( fetchEnvironments ( ) ) ;
178
+ } , [ dispatch ] ) ;
184
179
180
+ // Transform API data to match the UI expected format
185
181
const dataSource = environments . map ( ( env ) => ( {
186
- id : env . id ,
187
- name : env . name ,
188
- domain : env . domain ,
189
- stage : env . stage ,
182
+ id : env . environmentId ,
183
+ name : env . environmentType , // Using environmentType as name (adjust based on your actual data model)
184
+ domain : env . environmentType + ". domain.com" , // Placeholder domain (adjust based on your actual data)
185
+ stage : env . environmentType . toLowerCase ( ) ,
190
186
isMaster : env . isMaster ,
191
- hasApiKey : env . hasApiKey ,
192
- createTime : env . createTime ,
187
+ hasApiKey : ! ! env . environmentApikey ,
188
+ createTime : env . createdAt ,
193
189
del : environments . length > 1 && ! env . isMaster , // Only allow deletion if there's more than one environment and not master
194
190
} ) ) ;
195
191
@@ -210,6 +206,7 @@ function EnvironmentSetting() {
210
206
const handleApiKeyModalOpen = ( environment : DataItemInfo ) => {
211
207
setCurrentEnvironment ( environment ) ;
212
208
setIsApiKeyModalVisible ( true ) ;
209
+ apiKeyForm . resetFields ( ) ;
213
210
} ;
214
211
215
212
const handleApiKeyModalClose = ( ) => {
@@ -220,21 +217,37 @@ function EnvironmentSetting() {
220
217
const handleApiKeySubmit = ( ) => {
221
218
apiKeyForm . submit ( ) ;
222
219
apiKeyForm . validateFields ( ) . then ( ( values ) => {
223
- // Replace with actual action when available
224
- // dispatch(updateEnvironmentApiKeyAction({
225
- // environmentId: currentEnvironment.id,
226
- // apiKey: values.apiKey
227
- // }));
228
- console . log ( "Update API Key for environment" , currentEnvironment ?. id , values . apiKey ) ;
229
- handleApiKeyModalClose ( ) ;
220
+ if ( currentEnvironment ) {
221
+ dispatch ( updateEnvironmentApiKeyAction ( {
222
+ environmentId : currentEnvironment . id ,
223
+ apiKey : values . apiKey
224
+ } ) ) ;
225
+ handleApiKeyModalClose ( ) ;
226
+ }
230
227
} ) ;
231
228
} ;
232
229
230
+ if ( loading ) {
231
+ return (
232
+ < LoadingWrapper >
233
+ { /* You can add a Spinner component here */ }
234
+ Loading environments...
235
+ </ LoadingWrapper >
236
+ ) ;
237
+ }
238
+
239
+ if ( error ) {
240
+ return (
241
+ < div >
242
+ Error loading environments. Please try again.
243
+ </ div >
244
+ ) ;
245
+ }
246
+
233
247
return (
234
248
< Level1SettingPageContentWithList >
235
249
< Level1SettingPageTitleWithBtn >
236
250
{ trans ( "settings.environments" ) }
237
- { /* Adding API Keys is handled via existing environments */ }
238
251
</ Level1SettingPageTitleWithBtn >
239
252
< div >
240
253
< TableStyled
@@ -334,12 +347,15 @@ function EnvironmentSetting() {
334
347
e . stopPropagation ( ) ;
335
348
handleApiKeyModalOpen ( item ) ;
336
349
} }
350
+ loading = { apiKeyUpdating && currentEnvironment ?. id === item . id }
351
+ style = { { opacity : 1 } }
337
352
>
338
353
{ item . hasApiKey ? trans ( "environmentSettings.updateApiKey" ) : trans ( "environmentSettings.addApiKey" ) }
339
354
</ EditBtn >
340
355
< EditBtn
341
356
className = { "environment-edit-button" }
342
357
buttonType = { "primary" }
358
+ style = { { opacity : 1 } }
343
359
onClick = { ( e ) => {
344
360
e . stopPropagation ( ) ;
345
361
history . push ( buildEnvironmentId ( item . id ) ) ;
@@ -394,9 +410,7 @@ function EnvironmentSetting() {
394
410
return form . validateFields ( ) . then ( ( ) => {
395
411
const name = form . getFieldValue ( "name" ) ;
396
412
if ( name === item . name ) {
397
- // Replace with actual action when available
398
- // dispatch(deleteEnvironmentAction(item.id));
399
- console . log ( "Delete environment" , item . id ) ;
413
+ dispatch ( deleteEnvironmentAction ( item . id ) ) ;
400
414
form . resetFields ( ) ;
401
415
} else {
402
416
form . setFields ( [
@@ -425,18 +439,19 @@ function EnvironmentSetting() {
425
439
} ) ) }
426
440
/>
427
441
</ div >
428
-
442
+
429
443
{ /* API Key Modal */ }
430
444
< Modal
431
445
title = { currentEnvironment ?. hasApiKey
432
446
? trans ( "environmentSettings.updateApiKeyTitle" )
433
447
: trans ( "environmentSettings.addApiKeyTitle" ) }
434
- visible = { isApiKeyModalVisible }
448
+ open = { isApiKeyModalVisible }
435
449
onCancel = { handleApiKeyModalClose }
436
450
onOk = { handleApiKeySubmit }
437
451
okText = { currentEnvironment ?. hasApiKey
438
452
? trans ( "environmentSettings.updateApiKeyButton" )
439
453
: trans ( "environmentSettings.addApiKeyButton" ) }
454
+ confirmLoading = { apiKeyUpdating }
440
455
>
441
456
< Content >
442
457
< p > { transToNode ( "environmentSettings.apiKeyModalDescription" , {
@@ -464,4 +479,4 @@ function EnvironmentSetting() {
464
479
) ;
465
480
}
466
481
467
- export const EnvironmentList = EnvironmentSetting ;
482
+ export const EnvironmentList = EnvironmentSetting ;
0 commit comments