Skip to content

Commit 548e73b

Browse files
committed
update UI user groups tab
1 parent 3e35b5d commit 548e73b

File tree

1 file changed

+203
-82
lines changed

1 file changed

+203
-82
lines changed

client/packages/lowcoder/src/pages/setting/environments/components/UserGroupsTab.tsx

Lines changed: 203 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useState, useEffect } from 'react';
2-
import { Card, Button, Divider, Alert, message, Table, Tag, Input, Space } from 'antd';
3-
import { SyncOutlined, TeamOutlined } from '@ant-design/icons';
2+
import { Card, Button, Alert, message, Table, Tag, Input, Space, Row, Col, Avatar, Tooltip } from 'antd';
3+
import { SyncOutlined, TeamOutlined, UserOutlined, UsergroupAddOutlined, SettingOutlined, CodeOutlined } from '@ant-design/icons';
44
import Title from 'antd/lib/typography/Title';
55
import { Environment } from '../types/environment.types';
66
import { UserGroup, UserGroupsTabStats } from '../types/userGroup.types';
@@ -89,91 +89,166 @@ const UserGroupsTab: React.FC<UserGroupsTabProps> = ({ environment }) => {
8989
group.groupId.toLowerCase().includes(searchText.toLowerCase()))
9090
: userGroups;
9191

92+
// Helper function to generate colors from strings
93+
const stringToColor = (str: string) => {
94+
let hash = 0;
95+
for (let i = 0; i < str.length; i++) {
96+
hash = str.charCodeAt(i) + ((hash << 5) - hash);
97+
}
98+
99+
const hue = Math.abs(hash % 360);
100+
return `hsl(${hue}, 70%, 50%)`;
101+
};
102+
103+
// Stat card component
104+
const StatCard = ({ title, value, icon }: { title: string; value: number; icon: React.ReactNode }) => (
105+
<Card
106+
style={{
107+
height: '100%',
108+
borderRadius: '8px',
109+
boxShadow: '0 2px 8px rgba(0,0,0,0.05)'
110+
}}
111+
>
112+
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
113+
<div>
114+
<div style={{ fontSize: '14px', color: '#8c8c8c', marginBottom: '8px' }}>{title}</div>
115+
<div style={{ fontSize: '24px', fontWeight: 600 }}>{value}</div>
116+
</div>
117+
<div style={{
118+
fontSize: '28px',
119+
opacity: 0.8,
120+
color: '#722ed1',
121+
padding: '12px',
122+
backgroundColor: 'rgba(114, 46, 209, 0.1)',
123+
borderRadius: '50%',
124+
display: 'flex',
125+
alignItems: 'center',
126+
justifyContent: 'center'
127+
}}>
128+
{icon}
129+
</div>
130+
</div>
131+
</Card>
132+
);
133+
92134
// Table columns
93135
const columns = [
94136
{
95-
title: 'Name',
96-
dataIndex: 'groupName',
97-
key: 'groupName',
98-
render: (text: string) => <span className="group-name">{text}</span>
99-
},
100-
{
101-
title: 'ID',
102-
dataIndex: 'groupId',
103-
key: 'groupId',
104-
ellipsis: true,
137+
title: 'User Group',
138+
key: 'group',
139+
render: (group: UserGroup) => (
140+
<div style={{ display: 'flex', alignItems: 'center' }}>
141+
<Avatar
142+
style={{
143+
backgroundColor: stringToColor(group.groupName),
144+
marginRight: 12
145+
}}
146+
shape="square"
147+
>
148+
{group.groupName.charAt(0).toUpperCase()}
149+
</Avatar>
150+
<div>
151+
<div style={{ fontWeight: 500 }}>{group.groupName}</div>
152+
<div style={{ fontSize: 12, color: '#8c8c8c', marginTop: 4 }}>
153+
{group.groupId}
154+
</div>
155+
</div>
156+
</div>
157+
),
105158
},
106159
{
107160
title: 'Type',
108161
key: 'type',
109162
render: (_: any, group: UserGroup) => {
110-
if (group.allUsersGroup) return <Tag color="blue">All Users</Tag>;
111-
if (group.devGroup) return <Tag color="purple">Developers</Tag>;
112-
return <Tag color="default">Custom</Tag>;
163+
if (group.allUsersGroup) return (
164+
<Tag color="blue" style={{ borderRadius: '12px' }}>
165+
<UserOutlined style={{ marginRight: 4 }} /> All Users
166+
</Tag>
167+
);
168+
if (group.devGroup) return (
169+
<Tag color="purple" style={{ borderRadius: '12px' }}>
170+
<CodeOutlined style={{ marginRight: 4 }} /> Developers
171+
</Tag>
172+
);
173+
return (
174+
<Tag color="default" style={{ borderRadius: '12px' }}>
175+
<SettingOutlined style={{ marginRight: 4 }} /> Custom
176+
</Tag>
177+
);
113178
},
114179
},
115180
{
116181
title: 'Members',
117182
key: 'members',
118-
render: (_: any, group: UserGroup) => group.stats?.userCount || 0,
183+
render: (_: any, group: UserGroup) => (
184+
<Tooltip title="Total number of members in this group">
185+
<Tag style={{ borderRadius: '12px', backgroundColor: '#f6f6f6', color: '#333' }}>
186+
<UserOutlined style={{ marginRight: 4 }} /> {group.stats?.userCount || 0}
187+
</Tag>
188+
</Tooltip>
189+
),
119190
},
120191
{
121192
title: 'Admin Members',
122193
key: 'adminMembers',
123-
render: (_: any, group: UserGroup) => group.stats?.adminUserCount || 0,
194+
render: (_: any, group: UserGroup) => (
195+
<Tooltip title="Number of admin users in this group">
196+
<Tag style={{ borderRadius: '12px', backgroundColor: '#fff1f0', color: '#cf1322' }}>
197+
<UserOutlined style={{ marginRight: 4 }} /> {group.stats?.adminUserCount || 0}
198+
</Tag>
199+
</Tooltip>
200+
),
124201
},
125202
{
126203
title: 'Created',
127204
dataIndex: 'createTime',
128205
key: 'createTime',
129-
render: (createTime: number) => new Date(createTime).toLocaleDateString(),
206+
render: (createTime: number) => (
207+
<span style={{ color: '#8c8c8c' }}>
208+
{new Date(createTime).toLocaleDateString()}
209+
</span>
210+
),
130211
}
131212
];
132213

133214
return (
134-
<Card>
135-
{/* Header with refresh button */}
136-
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "16px" }}>
137-
<Title level={5}>User Groups in this Environment</Title>
215+
<div style={{ padding: '16px 0' }}>
216+
{/* Header */}
217+
<div style={{
218+
display: "flex",
219+
justifyContent: "space-between",
220+
alignItems: "center",
221+
marginBottom: "24px",
222+
background: 'linear-gradient(135deg, #722ed1 0%, #eb2f96 100%)',
223+
padding: '20px 24px',
224+
borderRadius: '8px',
225+
color: 'white'
226+
}}>
227+
<div>
228+
<Title level={4} style={{ color: 'white', margin: 0 }}>
229+
<UsergroupAddOutlined style={{ marginRight: 10 }} /> User Groups
230+
</Title>
231+
<p style={{ marginBottom: 0 }}>Manage user groups in this environment</p>
232+
</div>
138233
<Button
139234
icon={<SyncOutlined spin={refreshing} />}
140235
onClick={handleRefresh}
141236
loading={loading}
237+
type="primary"
238+
ghost
142239
>
143240
Refresh
144241
</Button>
145242
</div>
146243

147-
{/* Stats display */}
148-
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '24px', marginBottom: '16px' }}>
149-
<div>
150-
<div style={{ fontSize: '14px', color: '#8c8c8c' }}>Total Groups</div>
151-
<div style={{ fontSize: '24px', fontWeight: 600 }}>{stats.total}</div>
152-
</div>
153-
<div>
154-
<div style={{ fontSize: '14px', color: '#8c8c8c' }}>All Users Groups</div>
155-
<div style={{ fontSize: '24px', fontWeight: 600 }}>{stats.allUsers}</div>
156-
</div>
157-
<div>
158-
<div style={{ fontSize: '14px', color: '#8c8c8c' }}>Developer Groups</div>
159-
<div style={{ fontSize: '24px', fontWeight: 600 }}>{stats.developers}</div>
160-
</div>
161-
<div>
162-
<div style={{ fontSize: '14px', color: '#8c8c8c' }}>Custom Groups</div>
163-
<div style={{ fontSize: '24px', fontWeight: 600 }}>{stats.custom}</div>
164-
</div>
165-
</div>
166-
167-
<Divider style={{ margin: "16px 0" }} />
168-
169244
{/* Error display */}
170245
{error && (
171246
<Alert
172247
message="Error loading user groups"
173248
description={error}
174249
type="error"
175250
showIcon
176-
style={{ marginBottom: "16px" }}
251+
style={{ marginBottom: "20px" }}
177252
/>
178253
)}
179254

@@ -184,49 +259,95 @@ const UserGroupsTab: React.FC<UserGroupsTabProps> = ({ environment }) => {
184259
description="Missing required configuration: API key or API service URL"
185260
type="warning"
186261
showIcon
187-
style={{ marginBottom: "16px" }}
262+
style={{ marginBottom: "20px" }}
188263
/>
189264
)}
190265

266+
{/* Stats display */}
267+
<Row gutter={[16, 16]} style={{ marginBottom: '24px' }}>
268+
<Col xs={24} sm={12} md={6}>
269+
<StatCard
270+
title="Total Groups"
271+
value={stats.total}
272+
icon={<TeamOutlined />}
273+
/>
274+
</Col>
275+
<Col xs={24} sm={12} md={6}>
276+
<StatCard
277+
title="All Users Groups"
278+
value={stats.allUsers}
279+
icon={<UserOutlined />}
280+
/>
281+
</Col>
282+
<Col xs={24} sm={12} md={6}>
283+
<StatCard
284+
title="Developer Groups"
285+
value={stats.developers}
286+
icon={<CodeOutlined />}
287+
/>
288+
</Col>
289+
<Col xs={24} sm={12} md={6}>
290+
<StatCard
291+
title="Custom Groups"
292+
value={stats.custom}
293+
icon={<SettingOutlined />}
294+
/>
295+
</Col>
296+
</Row>
297+
191298
{/* Content */}
192-
{loading ? (
193-
<div style={{ display: 'flex', justifyContent: 'center', padding: '20px' }}>
194-
<Spin tip="Loading user groups..." />
195-
</div>
196-
) : userGroups.length === 0 ? (
197-
<Empty
198-
description={error || "No user groups found in this environment"}
199-
image={Empty.PRESENTED_IMAGE_SIMPLE}
200-
/>
201-
) : (
202-
<>
203-
{/* Search Bar */}
204-
<div style={{ marginBottom: 16 }}>
205-
<Search
206-
placeholder="Search user groups by name or ID"
207-
allowClear
208-
onSearch={value => setSearchText(value)}
209-
onChange={e => setSearchText(e.target.value)}
210-
style={{ width: 300 }}
211-
/>
212-
{searchText && filteredUserGroups.length !== userGroups.length && (
213-
<div style={{ marginTop: 8 }}>
214-
Showing {filteredUserGroups.length} of {userGroups.length} user groups
215-
</div>
216-
)}
299+
<Card
300+
style={{
301+
borderRadius: '8px',
302+
boxShadow: '0 2px 8px rgba(0,0,0,0.05)'
303+
}}
304+
>
305+
{loading ? (
306+
<div style={{ display: 'flex', justifyContent: 'center', padding: '40px' }}>
307+
<Spin size="large" tip="Loading user groups..." />
217308
</div>
218-
219-
<Table
220-
columns={columns}
221-
dataSource={filteredUserGroups}
222-
rowKey="groupId"
223-
pagination={{ pageSize: 10 }}
224-
size="middle"
225-
scroll={{ x: 'max-content' }}
309+
) : userGroups.length === 0 ? (
310+
<Empty
311+
description={error || "No user groups found in this environment"}
312+
image={Empty.PRESENTED_IMAGE_SIMPLE}
226313
/>
227-
</>
228-
)}
229-
</Card>
314+
) : (
315+
<>
316+
{/* Search Bar */}
317+
<div style={{ marginBottom: 20 }}>
318+
<Search
319+
placeholder="Search user groups by name or ID"
320+
allowClear
321+
onSearch={value => setSearchText(value)}
322+
onChange={e => setSearchText(e.target.value)}
323+
style={{ width: 300 }}
324+
size="large"
325+
/>
326+
{searchText && filteredUserGroups.length !== userGroups.length && (
327+
<div style={{ marginTop: 8, color: '#8c8c8c' }}>
328+
Showing {filteredUserGroups.length} of {userGroups.length} user groups
329+
</div>
330+
)}
331+
</div>
332+
333+
<Table
334+
columns={columns}
335+
dataSource={filteredUserGroups}
336+
rowKey="groupId"
337+
pagination={{
338+
pageSize: 10,
339+
showTotal: (total, range) => `${range[0]}-${range[1]} of ${total} user groups`
340+
}}
341+
style={{
342+
borderRadius: '8px',
343+
overflow: 'hidden'
344+
}}
345+
rowClassName={() => 'group-row'}
346+
/>
347+
</>
348+
)}
349+
</Card>
350+
</div>
230351
);
231352
};
232353

0 commit comments

Comments
 (0)