@@ -146,12 +146,6 @@ export const CreateEditRolePageView: FC<CreateEditRolePageViewProps> = ({
146
146
) ;
147
147
} ;
148
148
149
- interface ActionCheckboxesProps {
150
- permissions : readonly Permission [ ] | undefined ;
151
- form : ReturnType < typeof useFormik < Role > > & { values : Role } ;
152
- allResources : boolean ;
153
- }
154
-
155
149
const ResourceActionComparator = (
156
150
p : Permission ,
157
151
resource : string ,
@@ -160,6 +154,7 @@ const ResourceActionComparator = (
160
154
p . resource_type === resource &&
161
155
( p . action . toString ( ) === "*" || p . action === action ) ;
162
156
157
+ // the subset of resources that are useful for most users
163
158
const DEFAULT_RESOURCES = [
164
159
"audit_log" ,
165
160
"group" ,
@@ -177,6 +172,12 @@ const filteredRBACResourceActions = Object.fromEntries(
177
172
) ,
178
173
) ;
179
174
175
+ interface ActionCheckboxesProps {
176
+ permissions : readonly Permission [ ] ;
177
+ form : ReturnType < typeof useFormik < Role > > & { values : Role } ;
178
+ allResources : boolean ;
179
+ }
180
+
180
181
const ActionCheckboxes : FC < ActionCheckboxesProps > = ( {
181
182
permissions,
182
183
form,
@@ -185,6 +186,10 @@ const ActionCheckboxes: FC<ActionCheckboxesProps> = ({
185
186
const [ checkedActions , setCheckActions ] = useState ( permissions ) ;
186
187
const [ showAllResources , setShowAllResources ] = useState ( allResources ) ;
187
188
189
+ const resourceActions = showAllResources
190
+ ? RBACResourceActions
191
+ : filteredRBACResourceActions ;
192
+
188
193
const handleActionCheckChange = async (
189
194
e : ChangeEvent < HTMLInputElement > ,
190
195
form : ReturnType < typeof useFormik < Role > > & { values : Role } ,
@@ -194,7 +199,7 @@ const ActionCheckboxes: FC<ActionCheckboxesProps> = ({
194
199
195
200
const newPermissions = checked
196
201
? [
197
- ...( checkedActions ?? [ ] ) ,
202
+ ...checkedActions ,
198
203
{
199
204
negate : false ,
200
205
resource_type : resource_type as RBACResource ,
@@ -209,9 +214,36 @@ const ActionCheckboxes: FC<ActionCheckboxesProps> = ({
209
214
await form . setFieldValue ( "organization_permissions" , newPermissions ) ;
210
215
} ;
211
216
212
- const resourceActions = showAllResources
213
- ? RBACResourceActions
214
- : filteredRBACResourceActions ;
217
+ const handleResourceCheckChange = async (
218
+ e : ChangeEvent < HTMLInputElement > ,
219
+ form : ReturnType < typeof useFormik < Role > > & { values : Role } ,
220
+ indeterminate : boolean ,
221
+ ) => {
222
+ const { name, checked } = e . currentTarget ;
223
+ const resource = name as RBACResource ;
224
+
225
+ const resourceActionsForResource = resourceActions [ resource ] || { } ;
226
+
227
+ const newCheckedActions =
228
+ ! checked || indeterminate
229
+ ? checkedActions ?. filter ( ( p ) => p . resource_type !== resource )
230
+ : checkedActions ;
231
+
232
+ const newPermissions =
233
+ checked || indeterminate
234
+ ? [
235
+ ...newCheckedActions ,
236
+ ...Object . keys ( resourceActionsForResource ) . map ( ( resourceKey ) => ( {
237
+ negate : false ,
238
+ resource_type : resource as RBACResource ,
239
+ action : resourceKey as RBACAction ,
240
+ } ) ) ,
241
+ ]
242
+ : [ ...newCheckedActions ] ;
243
+
244
+ setCheckActions ( newPermissions ) ;
245
+ await form . setFieldValue ( "organization_permissions" , newPermissions ) ;
246
+ } ;
215
247
216
248
return (
217
249
< TableContainer >
@@ -233,36 +265,17 @@ const ActionCheckboxes: FC<ActionCheckboxesProps> = ({
233
265
< TableBody >
234
266
{ Object . entries ( resourceActions ) . map ( ( [ resourceKey , value ] ) => {
235
267
return (
236
- < TableRow key = { resourceKey } >
237
- < TableCell sx = { { paddingLeft : 2 } } colSpan = { 2 } >
238
- < li key = { resourceKey } css = { styles . checkBoxes } >
239
- { resourceKey }
240
- < ul css = { styles . checkBoxes } >
241
- { Object . entries ( value ) . map ( ( [ actionKey , value ] ) => (
242
- < li key = { actionKey } >
243
- < span css = { styles . actionText } >
244
- < Checkbox
245
- size = "small"
246
- name = { `${ resourceKey } :${ actionKey } ` }
247
- checked = { checkedActions ?. some ( ( p ) =>
248
- ResourceActionComparator (
249
- p ,
250
- resourceKey ,
251
- actionKey ,
252
- ) ,
253
- ) }
254
- onChange = { ( e ) => handleActionCheckChange ( e , form ) }
255
- />
256
- { actionKey }
257
- </ span > { " " }
258
- –{ " " }
259
- < span css = { styles . actionDescription } > { value } </ span >
260
- </ li >
261
- ) ) }
262
- </ ul >
263
- </ li >
264
- </ TableCell >
265
- </ TableRow >
268
+ < PermissionCheckboxGroup
269
+ key = { resourceKey }
270
+ checkedActions = { checkedActions ?. filter (
271
+ ( a ) => a . resource_type === resourceKey ,
272
+ ) }
273
+ resourceKey = { resourceKey }
274
+ value = { value }
275
+ form = { form }
276
+ handleActionCheckChange = { handleActionCheckChange }
277
+ handleResourceCheckChange = { handleResourceCheckChange }
278
+ />
266
279
) ;
267
280
} ) }
268
281
</ TableBody >
@@ -285,6 +298,77 @@ const ActionCheckboxes: FC<ActionCheckboxesProps> = ({
285
298
) ;
286
299
} ;
287
300
301
+ interface PermissionCheckboxGroupProps {
302
+ checkedActions : readonly Permission [ ] ;
303
+ resourceKey : string ;
304
+ value : Partial < Record < RBACAction , string > > ;
305
+ form : ReturnType < typeof useFormik < Role > > & { values : Role } ;
306
+ handleActionCheckChange : (
307
+ e : ChangeEvent < HTMLInputElement > ,
308
+ form : ReturnType < typeof useFormik < Role > > & { values : Role } ,
309
+ ) => Promise < void > ;
310
+ handleResourceCheckChange : (
311
+ e : ChangeEvent < HTMLInputElement > ,
312
+ form : ReturnType < typeof useFormik < Role > > & { values : Role } ,
313
+ indeterminate : boolean ,
314
+ ) => Promise < void > ;
315
+ }
316
+
317
+ const PermissionCheckboxGroup : FC < PermissionCheckboxGroupProps > = ( {
318
+ checkedActions,
319
+ resourceKey,
320
+ value,
321
+ form,
322
+ handleActionCheckChange,
323
+ handleResourceCheckChange,
324
+ } ) => {
325
+ return (
326
+ < TableRow key = { resourceKey } >
327
+ < TableCell sx = { { paddingLeft : 2 } } colSpan = { 2 } >
328
+ < li key = { resourceKey } css = { styles . checkBoxes } >
329
+ < Checkbox
330
+ size = "small"
331
+ name = { `${ resourceKey } ` }
332
+ checked = { checkedActions . length === Object . keys ( value ) . length }
333
+ indeterminate = {
334
+ checkedActions . length > 0 &&
335
+ checkedActions . length < Object . keys ( value ) . length
336
+ }
337
+ data-testid = { `${ resourceKey } ` }
338
+ onChange = { ( e ) =>
339
+ handleResourceCheckChange (
340
+ e ,
341
+ form ,
342
+ checkedActions . length > 0 &&
343
+ checkedActions . length < Object . keys ( value ) . length ,
344
+ )
345
+ }
346
+ />
347
+ { resourceKey }
348
+ < ul css = { styles . checkBoxes } >
349
+ { Object . entries ( value ) . map ( ( [ actionKey , value ] ) => (
350
+ < li key = { actionKey } css = { styles . actionItem } >
351
+ < span css = { styles . actionText } >
352
+ < Checkbox
353
+ size = "small"
354
+ name = { `${ resourceKey } :${ actionKey } ` }
355
+ checked = { checkedActions . some ( ( p ) =>
356
+ ResourceActionComparator ( p , resourceKey , actionKey ) ,
357
+ ) }
358
+ onChange = { ( e ) => handleActionCheckChange ( e , form ) }
359
+ />
360
+ { actionKey }
361
+ </ span >
362
+ < span css = { styles . actionDescription } > { value } </ span >
363
+ </ li >
364
+ ) ) }
365
+ </ ul >
366
+ </ li >
367
+ </ TableCell >
368
+ </ TableRow >
369
+ ) ;
370
+ } ;
371
+
288
372
interface ShowAllResourcesCheckboxProps {
289
373
showAllResources : boolean ;
290
374
setShowAllResources : React . Dispatch < React . SetStateAction < boolean > > ;
@@ -308,7 +392,13 @@ const ShowAllResourcesCheckbox: FC<ShowAllResourcesCheckboxProps> = ({
308
392
icon = { < VisibilityOffOutlinedIcon /> }
309
393
/>
310
394
}
311
- label = { < span style = { { fontSize : 12 } } > Show all permissions</ span > }
395
+ label = {
396
+ < span style = { { fontSize : 12 } } >
397
+ { showAllResources
398
+ ? "Hide advanced permissions"
399
+ : "Show advanced permissions" }
400
+ </ span >
401
+ }
312
402
/>
313
403
) ;
314
404
} ;
@@ -323,7 +413,12 @@ const styles = {
323
413
} ) ,
324
414
actionDescription : ( theme ) => ( {
325
415
color : theme . palette . text . secondary ,
416
+ paddingTop : 6 ,
326
417
} ) ,
418
+ actionItem : {
419
+ display : "grid" ,
420
+ gridTemplateColumns : "270px 1fr" ,
421
+ } ,
327
422
} satisfies Record < string , Interpolation < Theme > > ;
328
423
329
424
export default CreateEditRolePageView ;
0 commit comments