1
- import Button from "@material-ui/core/Button"
2
- import Link from "@material-ui/core/Link"
3
- import { makeStyles } from "@material-ui/core/styles"
4
- import Table from "@material-ui/core/Table"
5
- import TableBody from "@material-ui/core/TableBody"
6
- import TableCell from "@material-ui/core/TableCell"
7
- import TableContainer from "@material-ui/core/TableContainer"
8
- import TableHead from "@material-ui/core/TableHead"
9
- import TableRow from "@material-ui/core/TableRow"
10
- import ArrowRightAltOutlined from "@material-ui/icons/ArrowRightAltOutlined"
11
- import AddCircleOutline from "@material-ui/icons/AddCircleOutline"
12
- import KeyboardArrowRight from "@material-ui/icons/KeyboardArrowRight"
13
- import AvatarGroup from "@material-ui/lab/AvatarGroup"
14
1
import { useMachine } from "@xstate/react"
15
- import { AvatarData } from "components/AvatarData/AvatarData"
16
- import { ChooseOne , Cond } from "components/Conditionals/ChooseOne"
17
- import { EmptyState } from "components/EmptyState/EmptyState"
18
- import { Stack } from "components/Stack/Stack"
19
- import { TableLoader } from "components/TableLoader/TableLoader"
20
- import { UserAvatar } from "components/UserAvatar/UserAvatar"
21
2
import { useFeatureVisibility } from "hooks/useFeatureVisibility"
22
3
import { useOrganizationId } from "hooks/useOrganizationId"
23
4
import { usePermissions } from "hooks/usePermissions"
24
5
import React from "react"
25
6
import { Helmet } from "react-helmet-async"
26
- import { Link as RouterLink , useNavigate } from "react-router-dom"
27
7
import { pageTitle } from "util/page"
28
8
import { groupsMachine } from "xServices/groups/groupsXService"
29
- import { Paywall } from "components/Paywall/Paywall "
9
+ import GroupsPageView from "./GroupsPageView "
30
10
31
11
export const GroupsPage :React . FC = ( ) => {
32
12
const organizationId = useOrganizationId ( )
@@ -36,10 +16,6 @@ export const GroupsPage: React.FC = () => {
36
16
} ,
37
17
} )
38
18
const { groups} = state . context
39
- const isLoading = Boolean ( groups === undefined )
40
- const isEmpty = Boolean ( groups && groups . length === 0 )
41
- const navigate = useNavigate ( )
42
- const styles = useStyles ( )
43
19
const { createGroup :canCreateGroup } = usePermissions ( )
44
20
const { rbac :isRBACEnabled } = useFeatureVisibility ( )
45
21
@@ -49,157 +25,13 @@ export const GroupsPage: React.FC = () => {
49
25
< title > { pageTitle ( "Groups" ) } </ title >
50
26
</ Helmet >
51
27
52
- < ChooseOne >
53
- < Cond condition = { ! isRBACEnabled } >
54
- < Paywall
55
- message = "User groups"
56
- description = "Organize the users into groups and manage their permissions. To use this feature, you have to upgrade your account."
57
- cta = {
58
- < Stack direction = "row" alignItems = "center" >
59
- < Link
60
- underline = "none"
61
- href = "https://coder.com/docs/coder-oss/latest/admin/upgrade"
62
- target = "_blank"
63
- rel = "noreferrer"
64
- >
65
- < Button size = "small" startIcon = { < ArrowRightAltOutlined /> } >
66
- See how to upgrade
67
- </ Button >
68
- </ Link >
69
- < Link
70
- underline = "none"
71
- href = "https://coder.com/docs/coder-oss/latest/admin/upgrade"
72
- target = "_blank"
73
- rel = "noreferrer"
74
- >
75
- Read the docs
76
- </ Link >
77
- </ Stack >
78
- }
79
- />
80
- </ Cond >
81
- < Cond >
82
- < TableContainer >
83
- < Table >
84
- < TableHead >
85
- < TableRow >
86
- < TableCell width = "50%" > Name</ TableCell >
87
- < TableCell width = "49%" > Users</ TableCell >
88
- < TableCell width = "1%" > </ TableCell >
89
- </ TableRow >
90
- </ TableHead >
91
- < TableBody >
92
- < ChooseOne >
93
- < Cond condition = { isLoading } >
94
- < TableLoader />
95
- </ Cond >
96
-
97
- < Cond condition = { isEmpty } >
98
- < TableRow >
99
- < TableCell colSpan = { 999 } >
100
- < EmptyState
101
- message = "No groups yet"
102
- description = {
103
- canCreateGroup
104
- ?"Create your first group"
105
- :"You don't have permission to create a group"
106
- }
107
- cta = {
108
- canCreateGroup && (
109
- < Link underline = "none" component = { RouterLink } to = "/groups/create" >
110
- < Button startIcon = { < AddCircleOutline /> } > Create group</ Button >
111
- </ Link >
112
- )
113
- }
114
- />
115
- </ TableCell >
116
- </ TableRow >
117
- </ Cond >
118
-
119
- < Cond >
120
- { groups ?. map ( ( group ) => {
121
- const groupPageLink = `/groups/${ group . id } `
122
-
123
- return (
124
- < TableRow
125
- hover
126
- key = { group . id }
127
- data-testid = { `group-${ group . id } ` }
128
- tabIndex = { 0 }
129
- onClick = { ( ) => {
130
- navigate ( groupPageLink )
131
- } }
132
- onKeyDown = { ( event ) => {
133
- if ( event . key === "Enter" ) {
134
- navigate ( groupPageLink )
135
- }
136
- } }
137
- className = { styles . clickableTableRow }
138
- >
139
- < TableCell >
140
- < AvatarData
141
- title = { group . name }
142
- subtitle = { `${ group . members . length } members` }
143
- highlightTitle
144
- />
145
- </ TableCell >
146
-
147
- < TableCell >
148
- { group . members . length === 0 && "-" }
149
- < AvatarGroup >
150
- { group . members . map ( ( member ) => (
151
- < UserAvatar
152
- key = { member . username }
153
- username = { member . username }
154
- avatarURL = { member . avatar_url }
155
- />
156
- ) ) }
157
- </ AvatarGroup >
158
- </ TableCell >
159
-
160
- < TableCell >
161
- < div className = { styles . arrowCell } >
162
- < KeyboardArrowRight className = { styles . arrowRight } />
163
- </ div >
164
- </ TableCell >
165
- </ TableRow >
166
- )
167
- } ) }
168
- </ Cond >
169
- </ ChooseOne >
170
- </ TableBody >
171
- </ Table >
172
- </ TableContainer >
173
- </ Cond >
174
- </ ChooseOne >
28
+ < GroupsPageView
29
+ groups = { groups }
30
+ canCreateGroup = { canCreateGroup }
31
+ isRBACEnabled = { isRBACEnabled }
32
+ />
175
33
</ >
176
34
)
177
35
}
178
36
179
- const useStyles = makeStyles ( ( theme ) => ( {
180
- clickableTableRow :{
181
- cursor :"pointer" ,
182
-
183
- "&:hover td" :{
184
- backgroundColor :theme . palette . action . hover ,
185
- } ,
186
-
187
- "&:focus" :{
188
- outline :`1px solid${ theme . palette . secondary . dark } ` ,
189
- } ,
190
-
191
- "& .MuiTableCell-root:last-child" :{
192
- paddingRight :theme . spacing ( 2 ) ,
193
- } ,
194
- } ,
195
- arrowRight :{
196
- color :theme . palette . text . secondary ,
197
- width :20 ,
198
- height :20 ,
199
- } ,
200
- arrowCell :{
201
- display :"flex" ,
202
- } ,
203
- } ) )
204
-
205
37
export default GroupsPage