1
+ 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' ;
4
+ import Title from 'antd/lib/typography/Title' ;
5
+ import { Environment } from '../types/environment.types' ;
6
+ import { UserGroup } from '../types/userGroup.types' ;
7
+ import { getEnvironmentUserGroups } from '../services/environments.service' ;
8
+ import { Spin , Empty } from 'antd' ;
9
+
10
+ const { Search } = Input ;
11
+
12
+ interface UserGroupsTabProps {
13
+ environment : Environment ;
14
+ }
15
+
16
+ const UserGroupsTab : React . FC < UserGroupsTabProps > = ( { environment } ) => {
17
+ const [ userGroups , setUserGroups ] = useState < UserGroup [ ] > ( [ ] ) ;
18
+ const [ stats , setStats ] = useState ( {
19
+ total : 0 ,
20
+ active : 0 ,
21
+ inactive : 0
22
+ } ) ;
23
+ const [ loading , setLoading ] = useState ( false ) ;
24
+ const [ refreshing , setRefreshing ] = useState ( false ) ;
25
+ const [ error , setError ] = useState < string | null > ( null ) ;
26
+ const [ searchText , setSearchText ] = useState ( '' ) ;
27
+
28
+ // Fetch user groups
29
+ const fetchUserGroups = async ( ) => {
30
+ if ( ! environment ) return ;
31
+
32
+ setLoading ( true ) ;
33
+ setError ( null ) ;
34
+
35
+ try {
36
+ // Check for required environment properties
37
+ if ( ! environment . environmentApikey || ! environment . environmentApiServiceUrl ) {
38
+ setError ( 'Missing required configuration: API key or API service URL' ) ;
39
+ setLoading ( false ) ;
40
+ return ;
41
+ }
42
+
43
+ const groups = await getEnvironmentUserGroups (
44
+ environment . environmentId ,
45
+ environment . environmentApikey ,
46
+ environment . environmentApiServiceUrl
47
+ ) ;
48
+
49
+ setUserGroups ( groups ) ;
50
+
51
+ // Calculate stats
52
+ const total = groups . length ;
53
+ const active = groups . filter ( group => group . state === 'ACTIVE' ) . length ;
54
+
55
+ setStats ( {
56
+ total,
57
+ active,
58
+ inactive : total - active
59
+ } ) ;
60
+ } catch ( err ) {
61
+ setError ( err instanceof Error ? err . message : "Failed to fetch user groups" ) ;
62
+ } finally {
63
+ setLoading ( false ) ;
64
+ setRefreshing ( false ) ;
65
+ }
66
+ } ;
67
+
68
+ useEffect ( ( ) => {
69
+ fetchUserGroups ( ) ;
70
+ } , [ environment ] ) ;
71
+
72
+ // Handle refresh
73
+ const handleRefresh = ( ) => {
74
+ setRefreshing ( true ) ;
75
+ fetchUserGroups ( ) ;
76
+ } ;
77
+
78
+ // Filter user groups based on search
79
+ const filteredUserGroups = searchText
80
+ ? userGroups . filter ( group =>
81
+ group . name . toLowerCase ( ) . includes ( searchText . toLowerCase ( ) ) ||
82
+ group . id . toLowerCase ( ) . includes ( searchText . toLowerCase ( ) ) )
83
+ : userGroups ;
84
+
85
+ // Table columns
86
+ const columns = [
87
+ {
88
+ title : 'Name' ,
89
+ dataIndex : 'name' ,
90
+ key : 'name' ,
91
+ render : ( text : string ) => < span className = "group-name" > { text } </ span >
92
+ } ,
93
+ {
94
+ title : 'ID' ,
95
+ dataIndex : 'id' ,
96
+ key : 'id' ,
97
+ ellipsis : true ,
98
+ } ,
99
+ {
100
+ title : 'Type' ,
101
+ dataIndex : 'type' ,
102
+ key : 'type' ,
103
+ render : ( type : string ) => (
104
+ < Tag color = { type === 'USER' ? 'blue' : 'purple' } >
105
+ { type }
106
+ </ Tag >
107
+ ) ,
108
+ } ,
109
+ {
110
+ title : 'Status' ,
111
+ dataIndex : 'state' ,
112
+ key : 'state' ,
113
+ render : ( state : string ) => (
114
+ < Tag color = { state === 'ACTIVE' ? 'green' : 'red' } >
115
+ { state }
116
+ </ Tag >
117
+ ) ,
118
+ } ,
119
+ {
120
+ title : 'Member Count' ,
121
+ dataIndex : 'memberCount' ,
122
+ key : 'memberCount' ,
123
+ }
124
+ ] ;
125
+
126
+ return (
127
+ < Card >
128
+ { /* Header with refresh button */ }
129
+ < div style = { { display : "flex" , justifyContent : "space-between" , alignItems : "center" , marginBottom : "16px" } } >
130
+ < Title level = { 5 } > User Groups in this Environment</ Title >
131
+ < Button
132
+ icon = { < SyncOutlined spin = { refreshing } /> }
133
+ onClick = { handleRefresh }
134
+ loading = { loading }
135
+ >
136
+ Refresh
137
+ </ Button >
138
+ </ div >
139
+
140
+ { /* Stats display */ }
141
+ < div style = { { display : 'flex' , flexWrap : 'wrap' , gap : '24px' , marginBottom : '16px' } } >
142
+ < div >
143
+ < div style = { { fontSize : '14px' , color : '#8c8c8c' } } > Total Groups</ div >
144
+ < div style = { { fontSize : '24px' , fontWeight : 600 } } > { stats . total } </ div >
145
+ </ div >
146
+ < div >
147
+ < div style = { { fontSize : '14px' , color : '#8c8c8c' } } > Active</ div >
148
+ < div style = { { fontSize : '24px' , fontWeight : 600 } } > { stats . active } </ div >
149
+ </ div >
150
+ < div >
151
+ < div style = { { fontSize : '14px' , color : '#8c8c8c' } } > Inactive</ div >
152
+ < div style = { { fontSize : '24px' , fontWeight : 600 } } > { stats . inactive } </ div >
153
+ </ div >
154
+ </ div >
155
+
156
+ < Divider style = { { margin : "16px 0" } } />
157
+
158
+ { /* Error display */ }
159
+ { error && (
160
+ < Alert
161
+ message = "Error loading user groups"
162
+ description = { error }
163
+ type = "error"
164
+ showIcon
165
+ style = { { marginBottom : "16px" } }
166
+ />
167
+ ) }
168
+
169
+ { /* Configuration warnings */ }
170
+ { ( ! environment . environmentApikey || ! environment . environmentApiServiceUrl ) && ! error && (
171
+ < Alert
172
+ message = "Configuration Issue"
173
+ description = "Missing required configuration: API key or API service URL"
174
+ type = "warning"
175
+ showIcon
176
+ style = { { marginBottom : "16px" } }
177
+ />
178
+ ) }
179
+
180
+ { /* Content */ }
181
+ { loading ? (
182
+ < div style = { { display : 'flex' , justifyContent : 'center' , padding : '20px' } } >
183
+ < Spin tip = "Loading user groups..." />
184
+ </ div >
185
+ ) : userGroups . length === 0 ? (
186
+ < Empty
187
+ description = { error || "No user groups found in this environment" }
188
+ image = { Empty . PRESENTED_IMAGE_SIMPLE }
189
+ />
190
+ ) : (
191
+ < >
192
+ { /* Search Bar */ }
193
+ < div style = { { marginBottom : 16 } } >
194
+ < Search
195
+ placeholder = "Search user groups by name or ID"
196
+ allowClear
197
+ onSearch = { value => setSearchText ( value ) }
198
+ onChange = { e => setSearchText ( e . target . value ) }
199
+ style = { { width : 300 } }
200
+ />
201
+ { searchText && filteredUserGroups . length !== userGroups . length && (
202
+ < div style = { { marginTop : 8 } } >
203
+ Showing { filteredUserGroups . length } of { userGroups . length } user groups
204
+ </ div >
205
+ ) }
206
+ </ div >
207
+
208
+ < Table
209
+ columns = { columns }
210
+ dataSource = { filteredUserGroups }
211
+ rowKey = "id"
212
+ pagination = { { pageSize : 10 } }
213
+ size = "middle"
214
+ scroll = { { x : 'max-content' } }
215
+ />
216
+ </ >
217
+ ) }
218
+ </ Card >
219
+ ) ;
220
+ } ;
221
+
222
+ export default UserGroupsTab ;
0 commit comments