Skip to content

Commit 6fcafe4

Browse files
committed
update workspace header UI
1 parent 7660432 commit 6fcafe4

File tree

2 files changed

+268
-66
lines changed

2 files changed

+268
-66
lines changed

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

Lines changed: 11 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,15 @@ import history from "@lowcoder-ee/util/history";
33
import {
44
Spin,
55
Typography,
6-
Card,
76
Tabs,
8-
Button,
9-
Space,
10-
Tag,
11-
Switch,
127
message,
13-
Tooltip
148
} from "antd";
159
import {
1610
AppstoreOutlined,
1711
DatabaseOutlined,
1812
CodeOutlined,
1913
HomeOutlined,
2014
TeamOutlined,
21-
ArrowLeftOutlined,
22-
CloudUploadOutlined
2315
} from "@ant-design/icons";
2416

2517
// Use the context hooks
@@ -32,8 +24,8 @@ import AppsTab from "./components/AppsTab";
3224
import DataSourcesTab from "./components/DataSourcesTab";
3325
import QueriesTab from "./components/QueriesTab";
3426
import ModernBreadcrumbs from "./components/ModernBreadcrumbs";
27+
import WorkspaceHeader from "./components/WorkspaceHeader";
3528

36-
const { Title, Text } = Typography;
3729
const { TabPane } = Tabs;
3830

3931
const WorkspaceDetail: React.FC = () => {
@@ -42,6 +34,8 @@ const WorkspaceDetail: React.FC = () => {
4234
const { workspace, isLoading, error, toggleManagedStatus } = useWorkspaceContext();
4335
const { openDeployModal } = useDeployModal();
4436

37+
console.log("workspace render", workspace);
38+
4539
const [isToggling, setIsToggling] = useState(false);
4640

4741
// Handle toggle managed status
@@ -109,63 +103,14 @@ const WorkspaceDetail: React.FC = () => {
109103
{/* Modern Breadcrumbs navigation */}
110104
<ModernBreadcrumbs items={breadcrumbItems} />
111105

112-
{/* Workspace header with details and actions */}
113-
<Card style={{ marginBottom: "24px" }} bodyStyle={{ padding: "16px 24px" }}>
114-
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
115-
{/* Left section - Workspace info */}
116-
<div>
117-
<Title level={3} style={{ margin: 0 }}>
118-
{workspace.name}
119-
</Title>
120-
<div style={{ display: "flex", alignItems: "center", marginTop: "8px" }}>
121-
<Text type="secondary" style={{ marginRight: "16px" }}>
122-
ID: {workspace.id}
123-
</Text>
124-
<Tag color={workspace.managed ? "green" : "default"}>
125-
{workspace.managed ? "Managed" : "Unmanaged"}
126-
</Tag>
127-
</div>
128-
</div>
129-
130-
{/* Right section - Actions */}
131-
<Space size="middle">
132-
<div style={{ display: "flex", alignItems: "center" }}>
133-
<Text style={{ marginRight: "8px" }}>Managed:</Text>
134-
<Switch
135-
checked={!!workspace.managed}
136-
onChange={handleToggleManaged}
137-
loading={isToggling}
138-
checkedChildren="Yes"
139-
unCheckedChildren="No"
140-
/>
141-
</div>
142-
<Tooltip
143-
title={
144-
!workspace.managed
145-
? "Workspace must be managed before it can be deployed"
146-
: "Deploy this workspace to another environment"
147-
}
148-
>
149-
<Button
150-
type="primary"
151-
icon={<CloudUploadOutlined />}
152-
onClick={() =>
153-
openDeployModal(workspace, workspaceConfig, environment)
154-
}
155-
disabled={!workspace.managed}
156-
>
157-
Deploy
158-
</Button>
159-
</Tooltip>
160-
<Button
161-
icon={<ArrowLeftOutlined />}
162-
onClick={() => history.push(`/setting/environments/${environment.environmentId}`)}
163-
>
164-
Back
165-
</Button>
166-
</Space>
167-
</div>
168-
</Card>
106+
{/* New Workspace Header */}
107+
<WorkspaceHeader
108+
workspace={workspace}
109+
environment={environment}
110+
isToggling={isToggling}
111+
onToggleManagedStatus={handleToggleManaged}
112+
onDeploy={() => openDeployModal(workspace, workspaceConfig, environment)}
113+
/>
169114

170115
{/* Tabs for Apps, Data Sources, and Queries */}
171116
<Tabs defaultActiveKey="apps" className="modern-tabs" type="card">
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
import React, { useState } from "react";
2+
import {
3+
Typography,
4+
Switch,
5+
Button,
6+
Tag,
7+
Tooltip,
8+
Row,
9+
Col,
10+
Statistic,
11+
Avatar,
12+
Space,
13+
Divider,
14+
Card,
15+
Dropdown,
16+
Menu
17+
} from "antd";
18+
import {
19+
CloudUploadOutlined,
20+
SettingOutlined,
21+
TeamOutlined,
22+
AppstoreOutlined,
23+
DatabaseOutlined,
24+
CodeOutlined,
25+
CloudServerOutlined,
26+
ClockCircleOutlined,
27+
MoreOutlined,
28+
StarOutlined,
29+
StarFilled
30+
} from "@ant-design/icons";
31+
import { Environment } from "../types/environment.types";
32+
import { Workspace } from "../types/workspace.types";
33+
import styled from "styled-components";
34+
35+
const { Title, Text } = Typography;
36+
37+
// Styled components for custom design
38+
const HeaderWrapper = styled.div`
39+
border-radius: 12px;
40+
overflow: hidden;
41+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
42+
position: relative;
43+
margin-bottom: 24px;
44+
`;
45+
46+
const GradientBanner = styled.div`
47+
background: linear-gradient(135deg, #2b5876 0%, #4e4376 100%);
48+
height: 140px;
49+
position: relative;
50+
overflow: hidden;
51+
52+
&::before {
53+
content: '';
54+
position: absolute;
55+
top: -50%;
56+
left: -50%;
57+
width: 200%;
58+
height: 200%;
59+
background: repeating-linear-gradient(
60+
45deg,
61+
rgba(255,255,255,0.1),
62+
rgba(255,255,255,0.1) 1px,
63+
transparent 1px,
64+
transparent 10px
65+
);
66+
animation: moveBackground 30s linear infinite;
67+
}
68+
69+
@keyframes moveBackground {
70+
0% {
71+
transform: translate(0, 0);
72+
}
73+
100% {
74+
transform: translate(100px, 100px);
75+
}
76+
}
77+
`;
78+
79+
const ContentContainer = styled.div`
80+
background-color: white;
81+
padding: 24px;
82+
position: relative;
83+
`;
84+
85+
const AvatarContainer = styled.div`
86+
position: absolute;
87+
top: -50px;
88+
left: 24px;
89+
background: white;
90+
padding: 4px;
91+
border-radius: 16px;
92+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
93+
`;
94+
95+
const StatusBadge = styled(Tag)<{ $active?: boolean }>`
96+
position: absolute;
97+
top: 12px;
98+
right: 12px;
99+
font-weight: 600;
100+
font-size: 12px;
101+
padding: 4px 12px;
102+
border-radius: 20px;
103+
border: none;
104+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
105+
background: ${props => props.$active ? 'linear-gradient(135deg, #52c41a, #389e0d)' : '#f0f0f0'};
106+
color: ${props => props.$active ? 'white' : '#666'};
107+
`;
108+
109+
const StatCard = styled(Card)`
110+
border-radius: 8px;
111+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
112+
transition: all 0.3s;
113+
114+
&:hover {
115+
transform: translateY(-3px);
116+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
117+
}
118+
`;
119+
120+
const ActionButton = styled(Button)`
121+
border-radius: 8px;
122+
display: flex;
123+
align-items: center;
124+
justify-content: center;
125+
height: 38px;
126+
`;
127+
128+
const FavoriteButton = styled(Button)`
129+
position: absolute;
130+
top: 12px;
131+
right: 80px;
132+
border: none;
133+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
134+
`;
135+
136+
interface WorkspaceHeaderProps {
137+
workspace: Workspace;
138+
environment: Environment;
139+
isToggling: boolean;
140+
onToggleManagedStatus: (checked: boolean) => Promise<void>;
141+
onDeploy: () => void;
142+
}
143+
144+
const WorkspaceHeader: React.FC<WorkspaceHeaderProps> = ({
145+
workspace,
146+
environment,
147+
isToggling,
148+
onToggleManagedStatus,
149+
onDeploy
150+
}) => {
151+
152+
// Generate a consistent color for the workspace avatar
153+
const getAvatarColor = (name: string) => {
154+
let hash = 0;
155+
for (let i = 0; i < name.length; i++) {
156+
hash = name.charCodeAt(i) + ((hash << 5) - hash);
157+
}
158+
const hue = Math.abs(hash % 360);
159+
return `hsl(${hue}, 70%, 50%)`;
160+
};
161+
162+
// Format date for last updated
163+
const formatDate = (date: number | undefined) => {
164+
if (!date) return "N/A";
165+
return new Date(date).toLocaleDateString("en-US", {
166+
month: "short",
167+
day: "numeric",
168+
year: "numeric"
169+
});
170+
};
171+
172+
// Mock data - in a real app this would come from props
173+
const stats = {
174+
users: 12,
175+
apps: 24,
176+
datasources: 8,
177+
queries: 15,
178+
lastUpdated: "2023-08-15T10:30:00Z"
179+
};
180+
181+
182+
183+
return (
184+
<HeaderWrapper>
185+
<GradientBanner>
186+
<StatusBadge $active={workspace.managed}>
187+
{workspace.managed ? "Managed" : "Unmanaged"}
188+
</StatusBadge>
189+
190+
</GradientBanner>
191+
192+
<ContentContainer>
193+
<AvatarContainer>
194+
<Avatar
195+
size={80}
196+
style={{ backgroundColor: getAvatarColor(workspace.name) }}
197+
>
198+
{workspace.name.charAt(0).toUpperCase()}
199+
</Avatar>
200+
</AvatarContainer>
201+
202+
<Row>
203+
<Col xs={24} md={14} style={{ paddingLeft: '90px', marginBottom: '20px' }}>
204+
<Title level={3} style={{ margin: "0 0 8px 0" }}>
205+
{workspace.name}
206+
</Title>
207+
<Space size={16} wrap>
208+
<Text type="secondary">ID: {workspace.id}</Text>
209+
<Text type="secondary">
210+
<ClockCircleOutlined style={{ marginRight: 4 }} />
211+
created on {formatDate(workspace.creationDate)}
212+
</Text>
213+
<Tag color="blue">
214+
<CloudServerOutlined /> {environment.environmentName}
215+
</Tag>
216+
</Space>
217+
</Col>
218+
219+
<Col xs={24} md={10}>
220+
<div style={{ display: "flex", justifyContent: "flex-end", gap: "12px", flexWrap: "wrap" }}>
221+
<div style={{ display: "flex", alignItems: "center" }}>
222+
<Text style={{ marginRight: "8px" }}>Managed:</Text>
223+
<Switch
224+
checked={!!workspace.managed}
225+
onChange={onToggleManagedStatus}
226+
loading={isToggling}
227+
style={{ marginRight: 16 }}
228+
/>
229+
</div>
230+
<Tooltip
231+
title={
232+
!workspace.managed
233+
? "Workspace must be managed before it can be deployed"
234+
: "Deploy this workspace to another environment"
235+
}
236+
>
237+
<ActionButton
238+
type="primary"
239+
icon={<CloudUploadOutlined />}
240+
onClick={onDeploy}
241+
disabled={!workspace.managed}
242+
>
243+
Deploy
244+
</ActionButton>
245+
</Tooltip>
246+
</div>
247+
</Col>
248+
</Row>
249+
250+
251+
252+
</ContentContainer>
253+
</HeaderWrapper>
254+
);
255+
};
256+
257+
export default WorkspaceHeader;

0 commit comments

Comments
 (0)