@@ -146,12 +146,6 @@ export const CreateEditRolePageView: FC<CreateEditRolePageViewProps> = ({
146146 ) ;
147147} ;
148148
149- interface ActionCheckboxesProps {
150- permissions : readonly Permission [ ] | undefined ;
151- form : ReturnType < typeof useFormik < Role > > & { values : Role } ;
152- allResources : boolean ;
153- }
154-
155149const ResourceActionComparator = (
156150 p : Permission ,
157151 resource : string ,
@@ -160,6 +154,7 @@ const ResourceActionComparator = (
160154 p . resource_type === resource &&
161155 ( p . action . toString ( ) === "*" || p . action === action ) ;
162156
157+ // the subset of resources that are useful for most users
163158const DEFAULT_RESOURCES = [
164159 "audit_log" ,
165160 "group" ,
@@ -177,6 +172,12 @@ const filteredRBACResourceActions = Object.fromEntries(
177172 ) ,
178173) ;
179174
175+ interface ActionCheckboxesProps {
176+ permissions : readonly Permission [ ] ;
177+ form : ReturnType < typeof useFormik < Role > > & { values : Role } ;
178+ allResources : boolean ;
179+ }
180+
180181const ActionCheckboxes : FC < ActionCheckboxesProps > = ( {
181182 permissions,
182183 form,
@@ -185,6 +186,10 @@ const ActionCheckboxes: FC<ActionCheckboxesProps> = ({
185186 const [ checkedActions , setCheckActions ] = useState ( permissions ) ;
186187 const [ showAllResources , setShowAllResources ] = useState ( allResources ) ;
187188
189+ const resourceActions = showAllResources
190+ ? RBACResourceActions
191+ : filteredRBACResourceActions ;
192+
188193 const handleActionCheckChange = async (
189194 e : ChangeEvent < HTMLInputElement > ,
190195 form : ReturnType < typeof useFormik < Role > > & { values : Role } ,
@@ -194,7 +199,7 @@ const ActionCheckboxes: FC<ActionCheckboxesProps> = ({
194199
195200 const newPermissions = checked
196201 ? [
197- ...( checkedActions ?? [ ] ) ,
202+ ...checkedActions ,
198203 {
199204 negate : false ,
200205 resource_type : resource_type as RBACResource ,
@@ -209,9 +214,36 @@ const ActionCheckboxes: FC<ActionCheckboxesProps> = ({
209214 await form . setFieldValue ( "organization_permissions" , newPermissions ) ;
210215 } ;
211216
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+ } ;
215247
216248 return (
217249 < TableContainer >
@@ -233,36 +265,17 @@ const ActionCheckboxes: FC<ActionCheckboxesProps> = ({
233265 < TableBody >
234266 { Object . entries ( resourceActions ) . map ( ( [ resourceKey , value ] ) => {
235267 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+ />
266279 ) ;
267280 } ) }
268281 </ TableBody >
@@ -285,6 +298,77 @@ const ActionCheckboxes: FC<ActionCheckboxesProps> = ({
285298 ) ;
286299} ;
287300
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+
288372interface ShowAllResourcesCheckboxProps {
289373 showAllResources : boolean ;
290374 setShowAllResources : React . Dispatch < React . SetStateAction < boolean > > ;
@@ -308,7 +392,13 @@ const ShowAllResourcesCheckbox: FC<ShowAllResourcesCheckboxProps> = ({
308392 icon = { < VisibilityOffOutlinedIcon /> }
309393 />
310394 }
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+ }
312402 />
313403 ) ;
314404} ;
@@ -323,7 +413,12 @@ const styles = {
323413 } ) ,
324414 actionDescription : ( theme ) => ( {
325415 color : theme . palette . text . secondary ,
416+ paddingTop : 6 ,
326417 } ) ,
418+ actionItem : {
419+ display : "grid" ,
420+ gridTemplateColumns : "270px 1fr" ,
421+ } ,
327422} satisfies Record < string , Interpolation < Theme > > ;
328423
329424export default CreateEditRolePageView ;
0 commit comments