1- // environmentList.tsx - Main listing page for environments
2- import React , { useState } from "react" ;
1+ import React , { useEffect , useState } from "react" ;
32import { ADMIN_ROLE , SUPER_ADMIN_ROLE } from "constants/orgConstants" ;
43import { AddIcon , CustomModal , DangerIcon , EditPopover } from "lowcoder-design" ;
54import { 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" ;
810import styled from "styled-components" ;
911import { trans , transToNode } from "i18n" ;
1012import { buildEnvironmentId } from "constants/routesURL" ;
@@ -16,7 +18,10 @@ import {
1618} from "../permission/styledComponents" ;
1719import { Table } from "components/Table" ;
1820import history from "util/history" ;
19- import { Level1SettingPageContentWithList , Level1SettingPageTitleWithBtn } from "../styled" ;
21+ import {
22+ Level1SettingPageContentWithList ,
23+ Level1SettingPageTitleWithBtn ,
24+ } from "../styled" ;
2025import { timestampToHumanReadable } from "util/dateTimeUtils" ;
2126import { isSaasMode } from "util/envUtils" ;
2227import { selectSystemConfig } from "redux/selectors/configSelectors" ;
@@ -25,8 +30,12 @@ import { default as Input } from "antd/es/input";
2530import { default as Modal } from "antd/es/modal" ;
2631import { default as Tooltip } from "antd/es/tooltip" ;
2732import { 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" ;
3039import { StyledTag } from "./styledComponents" ;
3140
3241const EnvironmentName = styled . div `
@@ -42,7 +51,7 @@ const EnvironmentName = styled.div`
4251
4352const DomainName = styled . div `
4453 font-size: 13px;
45- color: #8B8FA3 ;
54+ color: #8b8fa3 ;
4655` ;
4756
4857const TableStyled = styled ( Table ) `
@@ -119,14 +128,22 @@ const ApiKeyStatusIcon = styled.div`
119128 display: inline-flex;
120129 align-items: center;
121130 margin-left: 8px;
122-
131+
123132 svg {
124133 width: 16px;
125134 height: 16px;
126- color: ${ props => props . color || "#8B8FA3" } ;
135+ color: ${ ( props ) => props . color || "#8B8FA3" } ;
127136 }
128137` ;
129138
139+
140+ const LoadingWrapper = styled . div `
141+ display: flex;
142+ justify-content: center;
143+ align-items: center;
144+ height: 200px;
145+ ` ;
146+
130147type DataItemInfo = {
131148 id : string ;
132149 name : string ;
@@ -139,57 +156,36 @@ type DataItemInfo = {
139156} ;
140157
141158function 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-
173159 const user = useSelector ( getUser ) ;
174- const environments = mockEnvironments ; // Replace with actual environments from Redux when available
175160 const dispatch = useDispatch ( ) ;
176161 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+
177169 const [ form ] = Form . useForm ( ) ;
178170 const [ apiKeyForm ] = Form . useForm ( ) ;
179171 const [ isApiKeyModalVisible , setIsApiKeyModalVisible ] = useState ( false ) ;
180- const [ currentEnvironment , setCurrentEnvironment ] = useState < DataItemInfo | null > ( null ) ;
172+ const [ currentEnvironment , setCurrentEnvironment ] =
173+ useState < DataItemInfo | null > ( null ) ;
181174
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 ] ) ;
184179
180+ // Transform API data to match the UI expected format
185181 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 ( ) ,
190186 isMaster : env . isMaster ,
191- hasApiKey : env . hasApiKey ,
192- createTime : env . createTime ,
187+ hasApiKey : ! ! env . environmentApikey ,
188+ createTime : env . createdAt ,
193189 del : environments . length > 1 && ! env . isMaster , // Only allow deletion if there's more than one environment and not master
194190 } ) ) ;
195191
@@ -210,6 +206,7 @@ function EnvironmentSetting() {
210206 const handleApiKeyModalOpen = ( environment : DataItemInfo ) => {
211207 setCurrentEnvironment ( environment ) ;
212208 setIsApiKeyModalVisible ( true ) ;
209+ apiKeyForm . resetFields ( ) ;
213210 } ;
214211
215212 const handleApiKeyModalClose = ( ) => {
@@ -220,21 +217,37 @@ function EnvironmentSetting() {
220217 const handleApiKeySubmit = ( ) => {
221218 apiKeyForm . submit ( ) ;
222219 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+ }
230227 } ) ;
231228 } ;
232229
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+
233247 return (
234248 < Level1SettingPageContentWithList >
235249 < Level1SettingPageTitleWithBtn >
236250 { trans ( "settings.environments" ) }
237- { /* Adding API Keys is handled via existing environments */ }
238251 </ Level1SettingPageTitleWithBtn >
239252 < div >
240253 < TableStyled
@@ -334,12 +347,15 @@ function EnvironmentSetting() {
334347 e . stopPropagation ( ) ;
335348 handleApiKeyModalOpen ( item ) ;
336349 } }
350+ loading = { apiKeyUpdating && currentEnvironment ?. id === item . id }
351+ style = { { opacity : 1 } }
337352 >
338353 { item . hasApiKey ? trans ( "environmentSettings.updateApiKey" ) : trans ( "environmentSettings.addApiKey" ) }
339354 </ EditBtn >
340355 < EditBtn
341356 className = { "environment-edit-button" }
342357 buttonType = { "primary" }
358+ style = { { opacity : 1 } }
343359 onClick = { ( e ) => {
344360 e . stopPropagation ( ) ;
345361 history . push ( buildEnvironmentId ( item . id ) ) ;
@@ -394,9 +410,7 @@ function EnvironmentSetting() {
394410 return form . validateFields ( ) . then ( ( ) => {
395411 const name = form . getFieldValue ( "name" ) ;
396412 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 ) ) ;
400414 form . resetFields ( ) ;
401415 } else {
402416 form . setFields ( [
@@ -425,18 +439,19 @@ function EnvironmentSetting() {
425439 } ) ) }
426440 />
427441 </ div >
428-
442+
429443 { /* API Key Modal */ }
430444 < Modal
431445 title = { currentEnvironment ?. hasApiKey
432446 ? trans ( "environmentSettings.updateApiKeyTitle" )
433447 : trans ( "environmentSettings.addApiKeyTitle" ) }
434- visible = { isApiKeyModalVisible }
448+ open = { isApiKeyModalVisible }
435449 onCancel = { handleApiKeyModalClose }
436450 onOk = { handleApiKeySubmit }
437451 okText = { currentEnvironment ?. hasApiKey
438452 ? trans ( "environmentSettings.updateApiKeyButton" )
439453 : trans ( "environmentSettings.addApiKeyButton" ) }
454+ confirmLoading = { apiKeyUpdating }
440455 >
441456 < Content >
442457 < p > { transToNode ( "environmentSettings.apiKeyModalDescription" , {
@@ -464,4 +479,4 @@ function EnvironmentSetting() {
464479 ) ;
465480}
466481
467- export const EnvironmentList = EnvironmentSetting ;
482+ export const EnvironmentList = EnvironmentSetting ;
0 commit comments