Skip to content

Commit 7f4d11f

Browse files
committed
update environment detail header
1 parent 548e73b commit 7f4d11f

File tree

3 files changed

+240
-69
lines changed

3 files changed

+240
-69
lines changed

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

Lines changed: 81 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,19 @@ import {
33
Spin,
44
Typography,
55
Card,
6-
Tag,
76
Tabs,
87
Alert,
98
Descriptions,
109
Menu,
1110
Button,
12-
Breadcrumb,
11+
Tag,
1312
} from "antd";
1413
import {
1514
LinkOutlined,
16-
TeamOutlined,
15+
HomeOutlined,
16+
AppstoreOutlined,
17+
UsergroupAddOutlined,
1718
EditOutlined,
18-
HomeOutlined
1919
} from "@ant-design/icons";
2020

2121
import { useSingleEnvironmentContext } from "./context/SingleEnvironmentContext";
@@ -24,6 +24,8 @@ import { Environment } from "./types/environment.types";
2424
import history from "@lowcoder-ee/util/history";
2525
import WorkspacesTab from "./components/WorkspacesTab";
2626
import UserGroupsTab from "./components/UserGroupsTab";
27+
import EnvironmentHeader from "./components/EnvironmentHeader";
28+
import ModernBreadcrumbs from "./components/ModernBreadcrumbs";
2729
const { Title, Text } = Typography;
2830
const { TabPane } = Tabs;
2931

@@ -42,6 +44,7 @@ const EnvironmentDetail: React.FC = () => {
4244

4345
const [isEditModalVisible, setIsEditModalVisible] = useState(false);
4446
const [isUpdating, setIsUpdating] = useState(false);
47+
const [activeTab, setActiveTab] = useState('workspaces');
4548

4649
// Handle edit menu item click
4750
const handleEditClick = () => {
@@ -90,21 +93,27 @@ const EnvironmentDetail: React.FC = () => {
9093
}
9194

9295
if (error || !environment) {
96+
const errorItems = [
97+
{
98+
key: 'environments',
99+
title: (
100+
<span>
101+
<HomeOutlined /> Environments
102+
</span>
103+
),
104+
onClick: () => history.push("/setting/environments")
105+
},
106+
{
107+
key: 'notFound',
108+
title: 'Not Found'
109+
}
110+
];
111+
93112
return (
94113
<div style={{ padding: "24px", flex: 1 }}>
95-
<Breadcrumb style={{ marginBottom: "16px" }}>
96-
<Breadcrumb.Item>
97-
<span
98-
style={{ cursor: "pointer" }}
99-
onClick={() => history.push("/setting/environments")}
100-
>
101-
<HomeOutlined /> Environments
102-
</span>
103-
</Breadcrumb.Item>
104-
<Breadcrumb.Item>Not Found</Breadcrumb.Item>
105-
</Breadcrumb>
114+
<ModernBreadcrumbs items={errorItems} />
106115

107-
<Card>
116+
<Card style={{ borderRadius: '8px', boxShadow: '0 2px 8px rgba(0,0,0,0.05)' }}>
108117
<div style={{ textAlign: "center", padding: "40px 0" }}>
109118
<Title level={3} style={{ color: "#ff4d4f" }}>
110119
Environment Not Found
@@ -124,58 +133,53 @@ const EnvironmentDetail: React.FC = () => {
124133
</div>
125134
);
126135
}
136+
137+
const breadcrumbItems = [
138+
{
139+
key: 'environments',
140+
title: (
141+
<span>
142+
<HomeOutlined /> Environments
143+
</span>
144+
),
145+
onClick: () => history.push("/setting/environments")
146+
},
147+
{
148+
key: 'currentEnvironment',
149+
title: environment.environmentName
150+
}
151+
];
127152

153+
// Get environment type tag color
154+
const getEnvironmentTypeColor = () => {
155+
switch(environment.environmentType) {
156+
case 'production':
157+
return 'red';
158+
case 'testing':
159+
return 'orange';
160+
default:
161+
return 'blue';
162+
}
163+
};
164+
128165
return (
129166
<div
130167
className="environment-detail-container"
131168
style={{ padding: "24px", flex: 1 }}
132169
>
133-
<Breadcrumb style={{ marginBottom: "16px" }}>
134-
<Breadcrumb.Item>
135-
<span
136-
style={{ cursor: "pointer" }}
137-
onClick={() => history.push("/setting/environments")}
138-
>
139-
<HomeOutlined /> Environments
140-
</span>
141-
</Breadcrumb.Item>
142-
<Breadcrumb.Item>{environment.environmentName}</Breadcrumb.Item>
143-
</Breadcrumb>
170+
<ModernBreadcrumbs items={breadcrumbItems} />
144171

145-
{/* Header with environment name and controls */}
146-
<div
147-
className="environment-header"
148-
style={{
149-
marginBottom: "24px",
150-
display: "flex",
151-
justifyContent: "space-between",
152-
alignItems: "flex-start",
153-
flexWrap: "wrap",
154-
gap: "16px",
155-
}}
156-
>
157-
<div style={{ flex: "1 1 auto", minWidth: "200px" }}>
158-
<Title level={3} style={{ margin: 0, wordBreak: "break-word" }}>
159-
{environment.environmentName || "Unnamed Environment"}
160-
</Title>
161-
<Text type="secondary">ID: {environment.environmentId}</Text>
162-
</div>
163-
<div style={{ flexShrink: 0 }}>
164-
<Button
165-
icon={<EditOutlined />}
166-
onClick={handleEditClick}
167-
type="primary"
168-
>
169-
Edit Environment
170-
</Button>
171-
</div>
172-
</div>
172+
{/* Environment Header Component */}
173+
<EnvironmentHeader
174+
environment={environment}
175+
onEditClick={handleEditClick}
176+
/>
173177

174178
{/* Basic Environment Information Card - improved responsiveness */}
175179
<Card
176180
title="Environment Overview"
177-
style={{ marginBottom: "24px" }}
178-
extra={environment.isMaster && <Tag color="green">Master</Tag>}
181+
style={{ marginBottom: "24px", borderRadius: '8px', boxShadow: '0 2px 8px rgba(0,0,0,0.05)' }}
182+
className="environment-overview-card"
179183
>
180184
<Descriptions
181185
bordered
@@ -198,22 +202,17 @@ const EnvironmentDetail: React.FC = () => {
198202
</Descriptions.Item>
199203
<Descriptions.Item label="Environment Type">
200204
<Tag
201-
color={
202-
environment.environmentType === "production"
203-
? "red"
204-
: environment.environmentType === "testing"
205-
? "orange"
206-
: "blue"
207-
}
205+
color={getEnvironmentTypeColor()}
206+
style={{ borderRadius: '12px' }}
208207
>
209208
{environment.environmentType}
210209
</Tag>
211210
</Descriptions.Item>
212211
<Descriptions.Item label="API Key Status">
213212
{environment.environmentApikey ? (
214-
<Tag color="green">Configured</Tag>
213+
<Tag color="green" style={{ borderRadius: '12px' }}>Configured</Tag>
215214
) : (
216-
<Tag color="red">Not Configured</Tag>
215+
<Tag color="red" style={{ borderRadius: '12px' }}>Not Configured</Tag>
217216
)}
218217
</Descriptions.Item>
219218
<Descriptions.Item label="Master Environment">
@@ -223,16 +222,29 @@ const EnvironmentDetail: React.FC = () => {
223222
</Card>
224223

225224
{/* Tabs for Workspaces and User Groups */}
226-
<Tabs defaultActiveKey="workspaces">
227-
<TabPane tab="Workspaces" key="workspaces">
225+
<Tabs
226+
defaultActiveKey="workspaces"
227+
activeKey={activeTab}
228+
onChange={setActiveTab}
229+
className="modern-tabs"
230+
type="card"
231+
>
232+
<TabPane
233+
tab={
234+
<span>
235+
<AppstoreOutlined /> Workspaces
236+
</span>
237+
}
238+
key="workspaces"
239+
>
228240
{/* Using our new standalone WorkspacesTab component */}
229241
<WorkspacesTab environment={environment} />
230242
</TabPane>
231243

232244
<TabPane
233245
tab={
234246
<span>
235-
<TeamOutlined /> User Groups
247+
<UsergroupAddOutlined /> User Groups
236248
</span>
237249
}
238250
key="userGroups"
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import React from 'react';
2+
import { Button, Tag, Typography, Row, Col } from 'antd';
3+
import { EditOutlined, EnvironmentOutlined } from '@ant-design/icons';
4+
import { Environment } from '../types/environment.types';
5+
6+
const { Title, Text } = Typography;
7+
8+
interface EnvironmentHeaderProps {
9+
environment: Environment;
10+
onEditClick: () => void;
11+
}
12+
13+
/**
14+
* Header component for environment details
15+
* Displays environment name, ID, type, and controls
16+
*/
17+
const EnvironmentHeader: React.FC<EnvironmentHeaderProps> = ({
18+
environment,
19+
onEditClick
20+
}) => {
21+
// Determine header gradient based on environment type
22+
const getHeaderGradient = () => {
23+
switch(environment.environmentType) {
24+
case 'production':
25+
return 'linear-gradient(135deg, #f5222d 0%, #fa8c16 100%)';
26+
case 'testing':
27+
return 'linear-gradient(135deg, #fa8c16 0%, #faad14 100%)';
28+
default:
29+
return 'linear-gradient(135deg, #1890ff 0%, #096dd9 100%)';
30+
}
31+
};
32+
33+
// Get environment type tag color
34+
const getEnvironmentTypeColor = () => {
35+
switch(environment.environmentType) {
36+
case 'production':
37+
return 'red';
38+
case 'testing':
39+
return 'orange';
40+
default:
41+
return 'blue';
42+
}
43+
};
44+
45+
return (
46+
<div
47+
className="environment-header"
48+
style={{
49+
marginBottom: "24px",
50+
background: getHeaderGradient(),
51+
padding: '20px 24px',
52+
borderRadius: '8px',
53+
color: 'white',
54+
boxShadow: '0 2px 8px rgba(0,0,0,0.1)'
55+
}}
56+
>
57+
<Row justify="space-between" align="middle" gutter={[16, 16]}>
58+
<Col xs={24} sm={18}>
59+
<div style={{ display: 'flex', alignItems: 'center', gap: '16px' }}>
60+
<div className="environment-icon" style={{
61+
fontSize: '32px',
62+
backgroundColor: 'rgba(255,255,255,0.2)',
63+
width: '64px',
64+
height: '64px',
65+
borderRadius: '50%',
66+
display: 'flex',
67+
alignItems: 'center',
68+
justifyContent: 'center'
69+
}}>
70+
<EnvironmentOutlined />
71+
</div>
72+
<div>
73+
<Title level={3} style={{ margin: '0 0 4px 0', color: 'white' }}>
74+
{environment.environmentName || "Unnamed Environment"}
75+
</Title>
76+
<div style={{ display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: '12px' }}>
77+
<Text style={{ color: 'rgba(255,255,255,0.85)' }}>
78+
ID: {environment.environmentId}
79+
</Text>
80+
<Tag
81+
color={getEnvironmentTypeColor()}
82+
style={{ marginLeft: 0, borderRadius: '12px' }}
83+
>
84+
{environment.environmentType}
85+
</Tag>
86+
{environment.isMaster && (
87+
<Tag color="green" style={{ marginLeft: 0, borderRadius: '12px' }}>
88+
Master
89+
</Tag>
90+
)}
91+
</div>
92+
</div>
93+
</div>
94+
</Col>
95+
<Col xs={24} sm={6} style={{ textAlign: 'right' }}>
96+
<Button
97+
icon={<EditOutlined />}
98+
onClick={onEditClick}
99+
type="primary"
100+
ghost
101+
size="large"
102+
>
103+
Edit Environment
104+
</Button>
105+
</Col>
106+
</Row>
107+
</div>
108+
);
109+
};
110+
111+
export default EnvironmentHeader;
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import React, { ReactNode } from 'react';
2+
import { Breadcrumb } from 'antd';
3+
import { BreadcrumbProps } from 'antd/lib/breadcrumb';
4+
5+
interface ModernBreadcrumbsProps extends BreadcrumbProps {
6+
/**
7+
* Items to display in the breadcrumb
8+
*/
9+
items?: {
10+
key: string;
11+
title: ReactNode;
12+
onClick?: () => void;
13+
}[];
14+
}
15+
16+
/**
17+
* Modern styled breadcrumb component with consistent styling
18+
*/
19+
const ModernBreadcrumbs: React.FC<ModernBreadcrumbsProps> = ({ items = [], ...props }) => {
20+
return (
21+
<div className="modern-breadcrumb" style={{
22+
background: '#f0f2f5',
23+
padding: '12px 24px',
24+
borderRadius: '8px',
25+
marginBottom: '20px',
26+
boxShadow: '0 1px 3px rgba(0,0,0,0.05)'
27+
}}>
28+
<Breadcrumb {...props}>
29+
{items.map(item => (
30+
<Breadcrumb.Item key={item.key}>
31+
{item.onClick ? (
32+
<span
33+
style={{ cursor: "pointer" }}
34+
onClick={item.onClick}
35+
>
36+
{item.title}
37+
</span>
38+
) : (
39+
item.title
40+
)}
41+
</Breadcrumb.Item>
42+
))}
43+
</Breadcrumb>
44+
</div>
45+
);
46+
};
47+
48+
export default ModernBreadcrumbs;

0 commit comments

Comments
 (0)