Skip to content

Commit b8c7625

Browse files
authored
Merge pull request #1606 from iamfaran/environments-only
Environments UI Screens - Thank you very much!
2 parents f3a3693 + fa725fa commit b8c7625

35 files changed

+4484
-4
lines changed

Diff for: client/packages/lowcoder/src/constants/routesURL.ts

+8
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ export const AUDIT_LOG_DETAIL = "/setting/audit/:eventId/detail";
2424
export const APP_USAGE_DASHBOARD = "/setting/app-usage";
2525
export const APP_USAGE_DETAIL = "/setting/app-usage/:eventId/detail";
2626

27+
export const ENVIRONMENT_SETTING = "/setting/environments";
28+
export const ENVIRONMENT_DETAIL = `${ENVIRONMENT_SETTING}/:environmentId`;
29+
export const ENVIRONMENT_WORKSPACE_DETAIL = `${ENVIRONMENT_DETAIL}/workspaces/:workspaceId`;
30+
2731
export const OAUTH_PROVIDER_SETTING = "/setting/oauth-provider";
2832
export const OAUTH_PROVIDER_DETAIL = "/setting/oauth-provider/detail";
2933

@@ -120,3 +124,7 @@ export const buildSubscriptionSettingsLink = (subscriptionId: string, productId
120124
export const buildSubscriptionInfoLink = (productId: string) => `${SUBSCRIPTION_SETTING}/info/${productId}`;
121125

122126
export const buildSupportTicketLink = (ticketId: string) => `${SUPPORT_URL}/details/${ticketId}`;
127+
128+
export const buildEnvironmentId = (environmentId: string) => `${ENVIRONMENT_SETTING}/${environmentId}`;
129+
export const buildEnvironmentWorkspaceId = (environmentId: string, workspaceId: string) =>
130+
`${ENVIRONMENT_SETTING}/${environmentId}/workspaces/${workspaceId}`;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
import React, {useState} from "react";
2+
import { useParams } from "react-router-dom";
3+
import {
4+
Spin,
5+
Typography,
6+
Card,
7+
Tag,
8+
Tabs,
9+
Alert,
10+
Descriptions,
11+
Dropdown,
12+
Menu,
13+
Button,
14+
Breadcrumb,
15+
} from "antd";
16+
import {
17+
ReloadOutlined,
18+
LinkOutlined,
19+
ClusterOutlined,
20+
TeamOutlined,
21+
UserOutlined,
22+
SyncOutlined,
23+
EditOutlined,
24+
EllipsisOutlined,
25+
MoreOutlined,
26+
HomeOutlined
27+
} from "@ant-design/icons";
28+
29+
import { useEnvironmentContext } from "./context/EnvironmentContext";
30+
import { workspaceConfig } from "./config/workspace.config";
31+
import { userGroupsConfig } from "./config/usergroups.config";
32+
import DeployableItemsTab from "./components/DeployableItemsTab";
33+
import EditEnvironmentModal from "./components/EditEnvironmentModal";
34+
import { Environment } from "./types/environment.types";
35+
import history from "@lowcoder-ee/util/history";
36+
37+
const { Title, Text } = Typography;
38+
const { TabPane } = Tabs;
39+
40+
41+
/**
42+
* Environment Detail Page Component
43+
* Shows detailed information about a specific environment
44+
*/
45+
const EnvironmentDetail: React.FC = () => {
46+
// Get environment ID from URL params
47+
const {
48+
environment,
49+
isLoadingEnvironment,
50+
error,
51+
updateEnvironmentData
52+
} = useEnvironmentContext();
53+
54+
55+
56+
const [isEditModalVisible, setIsEditModalVisible] = useState(false);
57+
const [isUpdating, setIsUpdating] = useState(false);
58+
59+
// Handle edit menu item click
60+
const handleEditClick = () => {
61+
setIsEditModalVisible(true);
62+
};
63+
64+
// Handle modal close
65+
const handleCloseModal = () => {
66+
setIsEditModalVisible(false);
67+
};
68+
69+
// Handle save environment
70+
const handleSaveEnvironment = async (environmentId: string, data: Partial<Environment>) => {
71+
setIsUpdating(true);
72+
try {
73+
await updateEnvironmentData(environmentId, data);
74+
handleCloseModal();
75+
} catch (error) {
76+
console.error('Failed to update environment:', error);
77+
} finally {
78+
setIsUpdating(false);
79+
}
80+
};
81+
82+
// Dropdown menu for environment actions
83+
const actionsMenu = (
84+
<Menu>
85+
<Menu.Item key="edit" icon={<EditOutlined />} onClick={handleEditClick}>
86+
Edit Environment
87+
</Menu.Item>
88+
{/* Add more menu items here if needed */}
89+
</Menu>
90+
);
91+
debugger
92+
93+
if (isLoadingEnvironment) {
94+
return (
95+
<div style={{ display: 'flex', justifyContent: 'center', padding: '50px' }}>
96+
<Spin size="large" tip="Loading environment..." />
97+
</div>
98+
);
99+
}
100+
101+
if (error || !environment) {
102+
return (
103+
<Alert
104+
message="Error loading environment"
105+
description={error || "Environment not found"}
106+
type="error"
107+
showIcon
108+
/>
109+
);
110+
}
111+
return (
112+
<div
113+
className="environment-detail-container"
114+
style={{ padding: "24px", flex: 1 }}
115+
>
116+
<Breadcrumb style={{ marginBottom: "16px" }}>
117+
<Breadcrumb.Item>
118+
<span
119+
style={{ cursor: "pointer" }}
120+
onClick={() => history.push("/setting/environments")}
121+
>
122+
<HomeOutlined /> Environments
123+
</span>
124+
</Breadcrumb.Item>
125+
<Breadcrumb.Item>{environment.environmentName}</Breadcrumb.Item>
126+
</Breadcrumb>
127+
128+
{/* Header with environment name and controls */}
129+
{/* Header with environment name and controls */}
130+
<div
131+
className="environment-header"
132+
style={{
133+
marginBottom: "24px",
134+
display: "flex",
135+
justifyContent: "space-between",
136+
alignItems: "flex-start",
137+
flexWrap: "wrap",
138+
gap: "16px",
139+
}}
140+
>
141+
<div style={{ flex: "1 1 auto", minWidth: "200px" }}>
142+
<Title level={3} style={{ margin: 0, wordBreak: "break-word" }}>
143+
{environment.environmentName || "Unnamed Environment"}
144+
</Title>
145+
<Text type="secondary">ID: {environment.environmentId}</Text>
146+
</div>
147+
<div style={{ flexShrink: 0 }}>
148+
<Button
149+
icon={<EditOutlined />}
150+
onClick={handleEditClick}
151+
type="primary"
152+
>
153+
Edit Environment
154+
</Button>
155+
</div>
156+
</div>
157+
158+
{/* Basic Environment Information Card - improved responsiveness */}
159+
<Card
160+
title="Environment Overview"
161+
style={{ marginBottom: "24px" }}
162+
extra={environment.isMaster && <Tag color="green">Master</Tag>}
163+
>
164+
<Descriptions
165+
bordered
166+
layout="vertical" // Change to vertical layout on smaller screens
167+
column={{ xxl: 4, xl: 3, lg: 3, md: 2, sm: 1, xs: 1 }}
168+
size="small" // Use smaller size on mobile
169+
>
170+
<Descriptions.Item label="Domain">
171+
{environment.environmentFrontendUrl ? (
172+
<a
173+
href={environment.environmentFrontendUrl}
174+
target="_blank"
175+
rel="noopener noreferrer"
176+
>
177+
{environment.environmentFrontendUrl} <LinkOutlined />
178+
</a>
179+
) : (
180+
"No domain set"
181+
)}
182+
</Descriptions.Item>
183+
<Descriptions.Item label="Environment Type">
184+
<Tag
185+
color={
186+
environment.environmentType === "production"
187+
? "red"
188+
: environment.environmentType === "testing"
189+
? "orange"
190+
: "blue"
191+
}
192+
>
193+
{environment.environmentType}
194+
</Tag>
195+
</Descriptions.Item>
196+
<Descriptions.Item label="API Key Status">
197+
{environment.environmentApikey ? (
198+
<Tag color="green">Configured</Tag>
199+
) : (
200+
<Tag color="red">Not Configured</Tag>
201+
)}
202+
</Descriptions.Item>
203+
<Descriptions.Item label="Master Environment">
204+
{environment.isMaster ? "Yes" : "No"}
205+
</Descriptions.Item>
206+
</Descriptions>
207+
</Card>
208+
209+
{/* Tabs for Workspaces and User Groups */}
210+
<Tabs defaultActiveKey="workspaces">
211+
<TabPane tab="Workspaces" key="workspaces">
212+
{/* Using our new generic component with the workspace config */}
213+
<DeployableItemsTab
214+
environment={environment}
215+
config={workspaceConfig}
216+
title="Workspaces in this Environment"
217+
/>
218+
</TabPane>
219+
<TabPane
220+
tab={
221+
<span>
222+
<TeamOutlined /> User Groups
223+
</span>
224+
}
225+
key="userGroups"
226+
>
227+
{/* Using our new generic component with the user group config */}
228+
<DeployableItemsTab
229+
environment={environment}
230+
config={userGroupsConfig}
231+
title="User Groups in this Environment"
232+
/>
233+
</TabPane>
234+
</Tabs>
235+
{/* Edit Environment Modal */}
236+
{environment && (
237+
<EditEnvironmentModal
238+
visible={isEditModalVisible}
239+
environment={environment}
240+
onClose={handleCloseModal}
241+
onSave={handleSaveEnvironment}
242+
loading={isUpdating}
243+
/>
244+
)}
245+
</div>
246+
);
247+
};
248+
249+
export default EnvironmentDetail;
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,34 @@
1-
export function Environments() {
2-
return <></>;
3-
}
1+
import React from "react";
2+
import { Switch, Route } from "react-router-dom";
3+
import { EnvironmentProvider } from "./context/EnvironmentContext";
4+
import EnvironmentsList from "./EnvironmentsList";
5+
import EnvironmentScopedRoutes from "./components/EnvironmentScopedRoutes";
6+
7+
import {
8+
ENVIRONMENT_SETTING,
9+
ENVIRONMENT_DETAIL
10+
} from "@lowcoder-ee/constants/routesURL";
11+
12+
/**
13+
* Top-level Environments component that wraps all environment-related routes
14+
* with the EnvironmentProvider for shared state management
15+
*/
16+
const Environments: React.FC = () => {
17+
return (
18+
<EnvironmentProvider>
19+
<Switch>
20+
{/* Route that shows the list of environments */}
21+
<Route exact path={ENVIRONMENT_SETTING}>
22+
<EnvironmentsList />
23+
</Route>
24+
25+
{/* All other routes under /environments/:envId */}
26+
<Route path={ENVIRONMENT_DETAIL}>
27+
<EnvironmentScopedRoutes />
28+
</Route>
29+
</Switch>
30+
</EnvironmentProvider>
31+
);
32+
};
33+
34+
export default Environments;

0 commit comments

Comments
 (0)