Skip to content

Commit c66a048

Browse files
committed
Add user groups in Env Detail Page
1 parent 3326214 commit c66a048

File tree

5 files changed

+358
-20
lines changed

5 files changed

+358
-20
lines changed

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

Lines changed: 89 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
} from "@ant-design/icons";
2525
import { useEnvironmentDetail } from "./hooks/useEnvironmentDetail";
2626
import WorkspacesList from "./components/WorkspacesList";
27+
import UserGroupsList from "./components/UserGroupsList";
2728

2829
const { Title, Text } = Typography;
2930
const { TabPane } = Tabs;
@@ -48,6 +49,12 @@ const EnvironmentDetail: React.FC = () => {
4849
workspacesError,
4950
refreshWorkspaces,
5051
workspaceStats,
52+
userGroups,
53+
userGroupsLoading,
54+
userGroupsError,
55+
refreshUserGroups,
56+
userGroupStats
57+
5158
} = useEnvironmentDetail(id);
5259
// If loading, show spinner
5360
if (loading) {
@@ -277,7 +284,6 @@ const EnvironmentDetail: React.FC = () => {
277284
/>
278285
</Card>
279286
</TabPane>
280-
281287
<TabPane
282288
tab={
283289
<span>
@@ -287,30 +293,102 @@ const EnvironmentDetail: React.FC = () => {
287293
key="userGroups"
288294
>
289295
<Card>
290-
{/* Placeholder for user group statistics */}
296+
{/* Header with refresh button */}
297+
<div
298+
style={{
299+
display: "flex",
300+
justifyContent: "space-between",
301+
alignItems: "center",
302+
marginBottom: "16px",
303+
}}
304+
>
305+
<Title level={5}>User Groups in this Environment</Title>
306+
<Button
307+
icon={<SyncOutlined />}
308+
onClick={refreshUserGroups}
309+
size="small"
310+
loading={userGroupsLoading}
311+
>
312+
Refresh User Groups
313+
</Button>
314+
</div>
315+
316+
{/* User Group Statistics */}
291317
<Row gutter={16} style={{ marginBottom: "24px" }}>
292318
<Col span={8}>
293319
<Statistic
294320
title="Total User Groups"
295-
value={0}
321+
value={userGroupStats.total}
296322
prefix={<TeamOutlined />}
297323
/>
298324
</Col>
299325
<Col span={8}>
300326
<Statistic
301327
title="Total Users"
302-
value={0}
328+
value={userGroupStats.totalUsers}
329+
prefix={<UserOutlined />}
330+
/>
331+
</Col>
332+
<Col span={8}>
333+
<Statistic
334+
title="Admin Users"
335+
value={userGroupStats.adminUsers}
303336
prefix={<UserOutlined />}
304337
/>
305338
</Col>
306339
</Row>
307340

308-
{/* Placeholder for user group list */}
309-
<Alert
310-
message="User Group Information"
311-
description="User group data will be implemented in the next phase. This section will display user group details and membership information."
312-
type="info"
313-
showIcon
341+
<Divider style={{ margin: "16px 0" }} />
342+
343+
{/* Show error if user group loading failed */}
344+
{userGroupsError && (
345+
<Alert
346+
message="Error loading user groups"
347+
description={userGroupsError}
348+
type="error"
349+
showIcon
350+
style={{ marginBottom: "16px" }}
351+
action={
352+
userGroupsError.includes("No API key configured") ||
353+
userGroupsError.includes("No API service URL configured") ? (
354+
<Button size="small" type="primary" disabled>
355+
Configuration Required
356+
</Button>
357+
) : (
358+
<Button
359+
size="small"
360+
type="primary"
361+
onClick={refreshUserGroups}
362+
>
363+
Try Again
364+
</Button>
365+
)
366+
}
367+
/>
368+
)}
369+
370+
{/* Show warning if no API key or API service URL is configured */}
371+
{(!environment.environmentApikey ||
372+
!environment.environmentApiServiceUrl) &&
373+
!userGroupsError && (
374+
<Alert
375+
message="Configuration Issue"
376+
description={
377+
!environment.environmentApikey
378+
? "An API key is required to fetch user groups for this environment."
379+
: "An API service URL is required to fetch user groups for this environment."
380+
}
381+
type="warning"
382+
showIcon
383+
style={{ marginBottom: "16px" }}
384+
/>
385+
)}
386+
387+
{/* User Groups List */}
388+
<UserGroupsList
389+
userGroups={userGroups}
390+
loading={userGroupsLoading && !userGroupsError}
391+
error={userGroupsError}
314392
/>
315393
</Card>
316394
</TabPane>
@@ -319,4 +397,4 @@ const EnvironmentDetail: React.FC = () => {
319397
);
320398
};
321399

322-
export default EnvironmentDetail;
400+
export default EnvironmentDetail;
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import React from 'react';
2+
import { Table, Tag, Empty, Spin, Badge } from 'antd';
3+
import { UserGroup } from '../types/userGroup.types';
4+
5+
interface UserGroupsListProps {
6+
userGroups: UserGroup[];
7+
loading: boolean;
8+
error?: string | null;
9+
}
10+
11+
/**
12+
* Component to display a list of user groups in a table
13+
*/
14+
const UserGroupsList: React.FC<UserGroupsListProps> = ({
15+
userGroups,
16+
loading,
17+
error,
18+
}) => {
19+
// Format timestamp to date string
20+
const formatDate = (timestamp?: number): string => {
21+
if (!timestamp) return 'N/A';
22+
const date = new Date(timestamp);
23+
return `${date.getMonth() + 1}/${date.getDate()}/${date.getFullYear()}`;
24+
};
25+
26+
// Table columns definition
27+
const columns = [
28+
{
29+
title: 'Name',
30+
dataIndex: 'groupName',
31+
key: 'groupName',
32+
render: (name: string, record: UserGroup) => (
33+
<div>
34+
<span>{name}</span>
35+
{record.allUsersGroup && (
36+
<Tag color="blue" style={{ marginLeft: 8 }}>All Users</Tag>
37+
)}
38+
{record.devGroup && (
39+
<Tag color="orange" style={{ marginLeft: 8 }}>Dev</Tag>
40+
)}
41+
</div>
42+
),
43+
},
44+
{
45+
title: 'ID',
46+
dataIndex: 'groupId',
47+
key: 'groupId',
48+
ellipsis: true,
49+
},
50+
{
51+
title: 'Users',
52+
key: 'userCount',
53+
render: (record: UserGroup) => (
54+
<div>
55+
<Badge count={record.stats.userCount} showZero style={{ backgroundColor: '#52c41a' }} />
56+
<span style={{ marginLeft: 8 }}>
57+
({record.stats.adminUserCount} admin{record.stats.adminUserCount !== 1 ? 's' : ''})
58+
</span>
59+
</div>
60+
),
61+
},
62+
{
63+
title: 'Created',
64+
key: 'createTime',
65+
render: (record: UserGroup) => formatDate(record.createTime),
66+
},
67+
{
68+
title: 'Type',
69+
key: 'type',
70+
render: (record: UserGroup) => {
71+
if (record.allUsersGroup) return <Tag color="blue">Global</Tag>;
72+
if (record.devGroup) return <Tag color="orange">Dev</Tag>;
73+
if (record.syncGroup) return <Tag color="purple">Sync</Tag>;
74+
return <Tag color="default">Standard</Tag>;
75+
},
76+
}
77+
];
78+
79+
// If loading, show spinner
80+
if (loading) {
81+
return (
82+
<div style={{ display: 'flex', justifyContent: 'center', padding: '20px' }}>
83+
<Spin tip="Loading user groups..." />
84+
</div>
85+
);
86+
}
87+
88+
// If no user groups or error, show empty state
89+
if (!userGroups || userGroups.length === 0 || error) {
90+
return (
91+
<Empty
92+
description={error || "No user groups found"}
93+
image={Empty.PRESENTED_IMAGE_SIMPLE}
94+
/>
95+
);
96+
}
97+
98+
return (
99+
<Table
100+
columns={columns}
101+
dataSource={userGroups}
102+
rowKey="groupId"
103+
pagination={{ pageSize: 10 }}
104+
size="middle"
105+
/>
106+
);
107+
};
108+
109+
export default UserGroupsList;

0 commit comments

Comments
 (0)