@@ -95,10 +95,10 @@ export const IdpSyncPageView: FC<IdpSyncPageViewProps> = ({
95
95
< Tabs active = { tab } >
96
96
< TabsList >
97
97
< TabLink to = "?tab=groups" value = "groups" >
98
- Group Sync Settings
98
+ Group sync settings
99
99
</ TabLink >
100
100
< TabLink to = "?tab=roles" value = "roles" >
101
- Role Sync Settings
101
+ Role sync settings
102
102
</ TabLink >
103
103
</ TabsList >
104
104
</ Tabs >
@@ -168,6 +168,7 @@ const IdpMappingTable: FC<IdpMappingTableProps> = ({
168
168
< TableRow >
169
169
< TableCell width = "45%" > IdP { type } </ TableCell >
170
170
< TableCell width = "55%" > Coder { type } </ TableCell >
171
+ < TableCell width = "10%" />
171
172
</ TableRow >
172
173
</ TableHead >
173
174
< TableBody >
@@ -206,13 +207,30 @@ const IdpMappingTable: FC<IdpMappingTableProps> = ({
206
207
) ;
207
208
} ;
208
209
209
- const GroupRow : FC < GroupRowProps > = ( { idpGroup, coderGroup } ) => {
210
+ interface GroupRowProps {
211
+ idpGroup : string ;
212
+ coderGroup : readonly string [ ] ;
213
+ onDelete : ( idpOrg : string ) => void ;
214
+ }
215
+
216
+ const GroupRow : FC < GroupRowProps > = ( { idpGroup, coderGroup, onDelete } ) => {
210
217
return (
211
218
< TableRow data-testid = { `group-${ idpGroup } ` } >
212
219
< TableCell > { idpGroup } </ TableCell >
213
220
< TableCell >
214
221
< IdpPillList roles = { coderGroup } />
215
222
</ TableCell >
223
+ < TableCell >
224
+ < Button
225
+ variant = "outline"
226
+ className = "w-8 h-8 px-1.5 py-1.5 text-content-secondary"
227
+ aria-label = "delete"
228
+ onClick = { ( ) => onDelete ( idpGroup ) }
229
+ >
230
+ < Trash />
231
+ < span className = "sr-only" > Delete IdP mapping</ span >
232
+ </ Button >
233
+ </ TableCell >
216
234
</ TableRow >
217
235
) ;
218
236
} ;
@@ -264,6 +282,20 @@ const IdpGroupSyncForm = ({
264
282
return groupIds . map ( ( groupId ) => groupsMap . get ( groupId ) || groupId ) ;
265
283
} ;
266
284
285
+ const handleDelete = async ( idpOrg : string ) => {
286
+ const newMapping = Object . fromEntries (
287
+ Object . entries ( form . values . mapping || { } ) . filter (
288
+ ( [ key ] ) => key !== idpOrg ,
289
+ ) ,
290
+ ) ;
291
+ const newSyncSettings = {
292
+ ...form . values ,
293
+ mapping : newMapping ,
294
+ } ;
295
+ void form . setFieldValue ( "mapping" , newSyncSettings . mapping ) ;
296
+ form . handleSubmit ( ) ;
297
+ } ;
298
+
267
299
const SYNC_FIELD_ID = "sync-field" ;
268
300
const REGEX_FILTER_ID = "regex-filter" ;
269
301
const AUTO_CREATE_MISSING_GROUPS_ID = "auto-create-missing-groups" ;
@@ -361,7 +393,7 @@ const IdpGroupSyncForm = ({
361
393
onChange = { setCoderGroups }
362
394
defaultOptions = { groups . map ( ( group ) => ( {
363
395
label : group . display_name || group . name ,
364
- value : group . name ,
396
+ value : group . id ,
365
397
} ) ) }
366
398
hidePlaceholderWhenSelected
367
399
placeholder = "Select group"
@@ -399,7 +431,7 @@ const IdpGroupSyncForm = ({
399
431
</ div >
400
432
</ div >
401
433
< div className = "flex gap-12" >
402
- < div className = "flex flex-col gap-4 w-full" >
434
+ < div className = "flex flex-col w-full" >
403
435
< IdpMappingTable type = "Group" isEmpty = { groupMappingCount === 0 } >
404
436
{ groupSyncSettings ?. mapping &&
405
437
Object . entries ( groupSyncSettings . mapping )
@@ -409,15 +441,18 @@ const IdpGroupSyncForm = ({
409
441
key = { idpGroup }
410
442
idpGroup = { idpGroup }
411
443
coderGroup = { getGroupNames ( groups ) }
444
+ onDelete = { handleDelete }
412
445
/>
413
446
) ) }
414
447
</ IdpMappingTable >
415
448
< div className = "flex justify-between" >
416
- < ExportPolicyButton
417
- syncSettings = { groupSyncSettings }
418
- organization = { organization }
419
- type = "groups"
420
- />
449
+ < span className = "pt-2" >
450
+ < ExportPolicyButton
451
+ syncSettings = { groupSyncSettings }
452
+ organization = { organization }
453
+ type = "groups"
454
+ />
455
+ </ span >
421
456
< TableRowCount count = { groupMappingCount } type = "groups" />
422
457
</ div >
423
458
</ div >
@@ -435,6 +470,7 @@ const IdpGroupSyncForm = ({
435
470
key = { idpGroup }
436
471
idpGroup = { idpGroup }
437
472
coderGroup = { getGroupNames ( [ groupId ] ) }
473
+ onDelete = { handleDelete }
438
474
/>
439
475
) ) }
440
476
</ IdpMappingTable >
@@ -482,6 +518,20 @@ const IdpRoleSyncForm = ({
482
518
const [ idpRoleName , setIdpRoleName ] = useState ( "" ) ;
483
519
const [ coderRoles , setCoderRoles ] = useState < Option [ ] > ( [ ] ) ;
484
520
521
+ const handleDelete = async ( idpOrg : string ) => {
522
+ const newMapping = Object . fromEntries (
523
+ Object . entries ( form . values . mapping || { } ) . filter (
524
+ ( [ key ] ) => key !== idpOrg ,
525
+ ) ,
526
+ ) ;
527
+ const newSyncSettings = {
528
+ ...form . values ,
529
+ mapping : newMapping ,
530
+ } ;
531
+ void form . setFieldValue ( "mapping" , newSyncSettings . mapping ) ;
532
+ form . handleSubmit ( ) ;
533
+ } ;
534
+
485
535
const SYNC_FIELD_ID = "sync-field" ;
486
536
const IDP_ROLE_NAME_ID = "idp-role-name" ;
487
537
@@ -592,15 +642,18 @@ const IdpRoleSyncForm = ({
592
642
key = { idpRole }
593
643
idpRole = { idpRole }
594
644
coderRoles = { roles }
645
+ onDelete = { handleDelete }
595
646
/>
596
647
) ) }
597
648
</ IdpMappingTable >
598
649
< div className = "flex justify-between" >
599
- < ExportPolicyButton
600
- syncSettings = { roleSyncSettings }
601
- organization = { organization }
602
- type = "roles"
603
- />
650
+ < span className = "pt-2" >
651
+ < ExportPolicyButton
652
+ syncSettings = { roleSyncSettings }
653
+ organization = { organization }
654
+ type = "roles"
655
+ />
656
+ </ span >
604
657
< TableRowCount count = { roleMappingCount } type = "roles" />
605
658
</ div >
606
659
</ div >
@@ -610,23 +663,30 @@ const IdpRoleSyncForm = ({
610
663
) ;
611
664
} ;
612
665
613
- interface GroupRowProps {
614
- idpGroup : string ;
615
- coderGroup : readonly string [ ] ;
616
- }
617
-
618
666
interface RoleRowProps {
619
667
idpRole : string ;
620
668
coderRoles : readonly string [ ] ;
669
+ onDelete : ( idpOrg : string ) => void ;
621
670
}
622
671
623
- const RoleRow : FC < RoleRowProps > = ( { idpRole, coderRoles } ) => {
672
+ const RoleRow : FC < RoleRowProps > = ( { idpRole, coderRoles, onDelete } ) => {
624
673
return (
625
674
< TableRow data-testid = { `role-${ idpRole } ` } >
626
675
< TableCell > { idpRole } </ TableCell >
627
676
< TableCell >
628
677
< IdpPillList roles = { coderRoles } />
629
678
</ TableCell >
679
+ < TableCell >
680
+ < Button
681
+ variant = "outline"
682
+ className = "w-8 h-8 px-1.5 py-1.5 text-content-secondary"
683
+ aria-label = "delete"
684
+ onClick = { ( ) => onDelete ( idpRole ) }
685
+ >
686
+ < Trash />
687
+ < span className = "sr-only" > Delete IdP mapping</ span >
688
+ </ Button >
689
+ </ TableCell >
630
690
</ TableRow >
631
691
) ;
632
692
} ;
0 commit comments