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

Lines changed: 80 additions & 65 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy