@@ -34,6 +34,15 @@ import {
3434 usePermissionGroups ,
3535} from '@/ee/access-control/hooks/permission-groups'
3636import { SettingRow } from '@/ee/components/setting-row'
37+ import {
38+ CREDENTIAL_EXPIRY_OPTIONS ,
39+ type CredentialExpiry ,
40+ type MappingTargetKind ,
41+ PERMISSION_OPTIONS ,
42+ SETTING_TOGGLES ,
43+ TARGET_KIND_OPTIONS ,
44+ type WorkspacePermission ,
45+ } from '@/ee/scim/constants'
3746import {
3847 useConfigureScimConnection ,
3948 useDeleteScimGroupMapping ,
@@ -50,51 +59,6 @@ interface ScimSectionProps {
5059 organizationId : string
5160}
5261
53- type MappingTargetKind = ScimGroupMappingView [ 'targetKind' ]
54- type WorkspacePermission = NonNullable < ScimGroupMappingView [ 'permissionType' ] >
55-
56- const TARGET_KIND_OPTIONS = [
57- { value : 'permission_group' , label : 'Permission group' } ,
58- { value : 'workspace' , label : 'Workspace' } ,
59- { value : 'org_role' , label : 'Organization admin' } ,
60- ] as const
61-
62- const PERMISSION_OPTIONS = [
63- { value : 'read' , label : 'Read' } ,
64- { value : 'write' , label : 'Write' } ,
65- { value : 'admin' , label : 'Admin' } ,
66- ] as const
67-
68- const SETTING_TOGGLES = [
69- {
70- key : 'lockManualMembership' ,
71- label : 'Lock managed membership' ,
72- description :
73- 'Refuse invitations, role changes, and manual grants for members the directory provisions. The next sync would revert them anyway.' ,
74- } ,
75- {
76- key : 'disableJit' ,
77- label : 'Disable just-in-time provisioning' ,
78- description :
79- 'Refuse membership for someone signing in with SSO who the directory never provisioned. The directory becomes the only way in.' ,
80- } ,
81- {
82- key : 'autoMapPermissionGroupsByName' ,
83- label : 'Match permission groups by name' ,
84- description :
85- 'When a pushed group has the same name as one of your permission groups, map them automatically. Nothing is created.' ,
86- } ,
87- ] as const
88-
89- /** Credential lifetimes offered at issue time; `never` matches what Okta and Entra expect by default. */
90- const CREDENTIAL_EXPIRY_OPTIONS = [
91- { value : 'never' , label : 'Never expires' } ,
92- { value : '90' , label : 'Expires in 90 days' } ,
93- { value : '365' , label : 'Expires in 1 year' } ,
94- ] as const
95-
96- type CredentialExpiry = ( typeof CREDENTIAL_EXPIRY_OPTIONS ) [ number ] [ 'value' ]
97-
9862const RELATIVE_TIME = new Intl . RelativeTimeFormat ( 'en' , { numeric : 'auto' } )
9963
10064/** Renders "3 minutes ago" for the activity list and credential rows. */
@@ -367,10 +331,8 @@ function ConnectionDetails({ organizationId, connection }: ConnectionDetailsProp
367331
368332 async function handleToggleSetting ( key : ( typeof SETTING_TOGGLES ) [ number ] [ 'key' ] , value : boolean ) {
369333 try {
370- await configure . mutateAsync ( {
371- organizationId,
372- settings : { ...connection . settings , [ key ] : value } ,
373- } )
334+ /** Only the changed key is sent; the server merges it, so a concurrent edit elsewhere is not reverted. */
335+ await configure . mutateAsync ( { organizationId, settings : { [ key ] : value } } )
374336 } catch ( error ) {
375337 toast . error ( getErrorMessage ( error , 'Failed to update setting' ) )
376338 }
0 commit comments