Skip to content

Commit 0697c93

Browse files
committed
Update UI for the EnvironmentsListing Page
1 parent 77e39d1 commit 0697c93

File tree

1 file changed

+73
-173
lines changed

1 file changed

+73
-173
lines changed

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

Lines changed: 73 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import React, { useState, useEffect, useRef } from "react";
2-
import { Typography, Alert, Input, Button, Space, Empty, Card, Spin, Row, Col, Tooltip, Badge } from "antd";
3-
import { SearchOutlined, CloudServerOutlined, SyncOutlined, PlusOutlined} from "@ant-design/icons";
1+
import React, { useState, useEffect } from "react";
2+
import { Alert, Empty, Spin } from "antd";
3+
import { SyncOutlined } from "@ant-design/icons";
4+
import { AddIcon, Search, TacoButton } from "lowcoder-design";
45
import { useHistory } from "react-router-dom";
56
import { useSelector, useDispatch } from "react-redux";
67
import { selectEnvironments, selectEnvironmentsLoading, selectEnvironmentsError } from "redux/selectors/enterpriseSelectors";
@@ -9,10 +10,49 @@ import { Environment } from "./types/environment.types";
910
import EnvironmentsTable from "./components/EnvironmentsTable";
1011
import CreateEnvironmentModal from "./components/CreateEnvironmentModal";
1112
import { buildEnvironmentId } from "@lowcoder-ee/constants/routesURL";
12-
import { getEnvironmentTagColor } from "./utils/environmentUtils";
1313
import { createEnvironment } from "./services/environments.service";
14-
15-
const { Title, Text } = Typography;
14+
import styled from "styled-components";
15+
16+
const EnvironmentsWrapper = styled.div`
17+
display: flex;
18+
flex-direction: column;
19+
width: 100%;
20+
height: 100%;
21+
`;
22+
23+
const HeaderWrapper = styled.div`
24+
display: flex;
25+
align-items: center;
26+
height: 92px;
27+
padding: 28px 36px;
28+
width: 100%;
29+
`;
30+
31+
const Title = styled.div`
32+
font-weight: 500;
33+
font-size: 18px;
34+
color: #222222;
35+
line-height: 18px;
36+
flex-grow: 1;
37+
`;
38+
39+
const AddBtn = styled(TacoButton)`
40+
min-width: 96px;
41+
width: fit-content;
42+
height: 32px;
43+
`;
44+
45+
const RefreshBtn = styled(TacoButton)`
46+
width: fit-content;
47+
height: 32px;
48+
margin-right: 12px;
49+
`;
50+
51+
const BodyWrapper = styled.div`
52+
width: 100%;
53+
flex-grow: 1;
54+
padding: 0 24px;
55+
`;
1656

1757
/**
1858
* Environment Listing Page Component
@@ -29,13 +69,10 @@ const EnvironmentsList: React.FC = () => {
2969
const [searchText, setSearchText] = useState("");
3070
const [isCreateModalVisible, setIsCreateModalVisible] = useState(false);
3171
const [isCreating, setIsCreating] = useState(false);
32-
33-
3472

3573
// Hook for navigation
3674
const history = useHistory();
3775

38-
3976
// Filter environments based on search text
4077
const filteredEnvironments = environments.filter((env) => {
4178
const searchLower = searchText.toLowerCase();
@@ -82,182 +119,45 @@ const EnvironmentsList: React.FC = () => {
82119
}
83120
};
84121

85-
// Count environment types
86-
const environmentCounts = environments.reduce((counts, env) => {
87-
const type = env.environmentType.toUpperCase();
88-
counts[type] = (counts[type] || 0) + 1;
89-
return counts;
90-
}, {} as Record<string, number>);
91-
92122
return (
93-
<div
94-
className="environments-container"
95-
style={{
96-
padding: "24px",
97-
flex: 1,
98-
minWidth: "1000px",
99-
// Ensure minimum width to prevent excessive shrinking
100-
}}
101-
>
102-
{/* Modern gradient header */}
103-
<div
104-
className="environments-header"
105-
style={{
106-
marginBottom: "24px",
107-
background: 'linear-gradient(135deg, #0050b3 0%, #1890ff 100%)',
108-
padding: '24px 32px',
109-
borderRadius: '12px',
110-
color: 'white',
111-
boxShadow: '0 4px 12px rgba(0,0,0,0.1)'
112-
}}
113-
>
114-
<Row justify="space-between" align="middle" gutter={[16, 16]}>
115-
<Col xs={24} sm={16}>
116-
<div style={{ display: 'flex', alignItems: 'center', gap: '20px' }}>
117-
<div style={{
118-
fontSize: '36px',
119-
backgroundColor: 'rgba(255,255,255,0.2)',
120-
width: '72px',
121-
height: '72px',
122-
borderRadius: '50%',
123-
display: 'flex',
124-
alignItems: 'center',
125-
justifyContent: 'center',
126-
boxShadow: '0 4px 8px rgba(0,0,0,0.1)'
127-
}}>
128-
<CloudServerOutlined />
129-
</div>
130-
<div>
131-
<Title level={2} style={{ margin: '0 0 4px 0', color: 'white' }}>
132-
Environments
133-
</Title>
134-
<Text style={{ color: 'rgba(255,255,255,0.85)', fontSize: '16px' }}>
135-
Manage your deployment environments across dev, test, preprod, and production
136-
</Text>
137-
</div>
138-
</div>
139-
</Col>
140-
<Col xs={24} sm={8} style={{ textAlign: 'right' }}>
141-
<Space size="middle">
142-
<Button
143-
icon={<PlusOutlined />}
144-
onClick={() => setIsCreateModalVisible(true)}
145-
type="primary"
146-
style={{
147-
backgroundColor: 'rgba(255, 255, 255, 0.15)',
148-
borderColor: 'rgba(255, 255, 255, 0.4)',
149-
color: 'white',
150-
fontWeight: 500
151-
}}
152-
>
153-
Create Environment
154-
</Button>
155-
<Button
156-
icon={<SyncOutlined spin={isLoading} />}
157-
onClick={handleRefresh}
158-
loading={isLoading}
159-
type="default"
160-
style={{
161-
backgroundColor: 'rgba(255, 255, 255, 0.2)',
162-
borderColor: 'rgba(255, 255, 255, 0.4)',
163-
color: 'white',
164-
fontWeight: 500
165-
}}
166-
>
167-
Refresh
168-
</Button>
169-
</Space>
170-
</Col>
171-
</Row>
172-
</div>
173-
174-
{/* Environment type stats */}
175-
{environments.length > 0 && (
176-
<Row gutter={[16, 16]} style={{ marginBottom: '24px' }}>
177-
<Col span={24}>
178-
<Card
179-
title="Environment Overview"
180-
style={{
181-
borderRadius: '12px',
182-
boxShadow: '0 2px 8px rgba(0,0,0,0.05)'
183-
}}
184-
styles={{ header: { borderBottom: '1px solid #f0f0f0', padding: '16px 24px' } }}
185-
bodyStyle={{ padding: '24px' }}
186-
>
187-
<Row gutter={[32, 16]} justify="space-around">
188-
<Col>
189-
<Tooltip title="Total number of environments">
190-
<div style={{ textAlign: 'center' }}>
191-
<div style={{ fontSize: '38px', fontWeight: 600, color: '#1890ff' }}>
192-
{environments.length}
193-
</div>
194-
<div style={{ fontSize: '14px', color: '#8c8c8c', marginTop: '4px' }}>
195-
Total Environments
196-
</div>
197-
</div>
198-
</Tooltip>
199-
</Col>
200-
201-
{['PROD', 'PREPROD', 'TEST', 'DEV'].map(type => (
202-
<Col key={type}>
203-
<Tooltip title={`Number of ${type} environments`}>
204-
<div style={{ textAlign: 'center' }}>
205-
<div style={{
206-
fontSize: '38px',
207-
fontWeight: 600,
208-
color: getEnvironmentTagColor(type) === 'default' ? '#8c8c8c' : getEnvironmentTagColor(type)
209-
}}>
210-
{environmentCounts[type] || 0}
211-
</div>
212-
<div style={{ fontSize: '14px', color: '#8c8c8c', marginTop: '4px', display: 'flex', justifyContent: 'center', alignItems: 'center', gap: '6px' }}>
213-
<Badge color={getEnvironmentTagColor(type)} />
214-
{type} Environments
215-
</div>
216-
</div>
217-
</Tooltip>
218-
</Col>
219-
))}
220-
</Row>
221-
</Card>
222-
</Col>
223-
</Row>
224-
)}
225-
226-
{/* Main content card */}
227-
<Card
228-
title="Environment List"
229-
style={{
230-
borderRadius: '12px',
231-
boxShadow: '0 2px 8px rgba(0,0,0,0.05)',
232-
}}
233-
styles={{ header: { borderBottom: '1px solid #f0f0f0', padding: '16px 24px' } }}
234-
bodyStyle={{ padding: '24px' }}
235-
extra={
236-
<Input
237-
placeholder="Search environments"
238-
value={searchText}
239-
onChange={(e) => setSearchText(e.target.value)}
240-
style={{ width: 250 }}
241-
prefix={<SearchOutlined />}
242-
allowClear
243-
/>
244-
}
245-
>
123+
<EnvironmentsWrapper>
124+
<HeaderWrapper>
125+
<Title>Environments</Title>
126+
<Search
127+
placeholder="Search"
128+
value={searchText}
129+
onChange={(e) => setSearchText(e.target.value)}
130+
style={{ width: "192px", height: "32px", margin: "0 12px 0 0" }}
131+
/>
132+
<RefreshBtn
133+
buttonType="normal"
134+
icon={<SyncOutlined spin={isLoading} />}
135+
onClick={handleRefresh}
136+
loading={isLoading}
137+
>
138+
Refresh
139+
</RefreshBtn>
140+
<AddBtn buttonType="primary" onClick={() => setIsCreateModalVisible(true)}>
141+
New Environment
142+
</AddBtn>
143+
</HeaderWrapper>
144+
145+
<BodyWrapper>
246146
{/* Error handling */}
247147
{error && (
248148
<Alert
249149
message="Error loading environments"
250150
description={error}
251151
type="error"
252152
showIcon
253-
style={{ marginBottom: "24px" }}
153+
style={{ marginBottom: "16px" }}
254154
/>
255155
)}
256156

257157
{/* Loading, empty state or table */}
258158
{isLoading ? (
259159
<div style={{ display: 'flex', justifyContent: 'center', padding: '60px 0' }}>
260-
<Spin size="large" tip="Loading environments..." />
160+
<Spin size="large" />
261161
</div>
262162
) : environments.length === 0 && !error ? (
263163
<Empty
@@ -286,7 +186,7 @@ const EnvironmentsList: React.FC = () => {
286186
Showing {filteredEnvironments.length} of {environments.length} environments
287187
</div>
288188
)}
289-
</Card>
189+
</BodyWrapper>
290190

291191
{/* Create Environment Modal */}
292192
<CreateEnvironmentModal
@@ -295,7 +195,7 @@ const EnvironmentsList: React.FC = () => {
295195
onSave={handleCreateEnvironment}
296196
loading={isCreating}
297197
/>
298-
</div>
198+
</EnvironmentsWrapper>
299199
);
300200
};
301201

0 commit comments

Comments
 (0)