Skip to content

Commit dab7dff

Browse files
committed
integrate API in environments listing page
1 parent 6e8c641 commit dab7dff

File tree

1 file changed

+80
-65
lines changed

1 file changed

+80
-65
lines changed

client/packages/lowcoder/src/pages/setting/environments/environmentList.tsx

+80-65
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
// environmentList.tsx - Main listing page for environments
2-
import React, { useState } from "react";
1+
import React, { useEffect, useState } from "react";
32
import { ADMIN_ROLE, SUPER_ADMIN_ROLE } from "constants/orgConstants";
43
import { AddIcon, CustomModal, DangerIcon, EditPopover } from "lowcoder-design";
54
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";
810
import styled from "styled-components";
911
import { trans, transToNode } from "i18n";
1012
import { buildEnvironmentId } from "constants/routesURL";
@@ -16,7 +18,10 @@ import {
1618
} from "../permission/styledComponents";
1719
import { Table } from "components/Table";
1820
import history from "util/history";
19-
import { Level1SettingPageContentWithList, Level1SettingPageTitleWithBtn } from "../styled";
21+
import {
22+
Level1SettingPageContentWithList,
23+
Level1SettingPageTitleWithBtn,
24+
} from "../styled";
2025
import { timestampToHumanReadable } from "util/dateTimeUtils";
2126
import { isSaasMode } from "util/envUtils";
2227
import { selectSystemConfig } from "redux/selectors/configSelectors";
@@ -25,8 +30,12 @@ import { default as Input } from "antd/es/input";
2530
import { default as Modal } from "antd/es/modal";
2631
import { default as Tooltip } from "antd/es/tooltip";
2732
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";
3039
import { StyledTag } from "./styledComponents";
3140

3241
const EnvironmentName = styled.div`
@@ -42,7 +51,7 @@ const EnvironmentName = styled.div`
4251

4352
const DomainName = styled.div`
4453
font-size: 13px;
45-
color: #8B8FA3;
54+
color: #8b8fa3;
4655
`;
4756

4857
const 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+
130147
type DataItemInfo = {
131148
id: string;
132149
name: string;
@@ -139,57 +156,36 @@ type DataItemInfo = {
139156
};
140157

141158
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-
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

Comments
 (0)