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

client/packages/lowcoder/src/constants/routesURL.ts

Lines changed: 8 additions & 0 deletions
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}`;
Lines changed: 249 additions & 0 deletions
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;
Lines changed: 34 additions & 3 deletions
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)
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