@@ -109,15 +109,27 @@ export function SlackManagedUsersModal({
109109 availableApps . find ( ( app ) => app . appId === appId ) ??
110110 ( availableApps . length === 1 && ! appId ? availableApps [ 0 ] : undefined )
111111 const sharedAppInstalled = organizationSetup && selectedApp ?. appKind === 'shared'
112+ const noInstalledApp = organizationSetup && apps . isSuccess && availableApps . length === 0
113+ /** The shared app connects member accounts without a Search bot installation. */
114+ const sharedAppWithoutBot = noInstalledApp && Boolean ( apps . data ?. sharedAppAvailable )
115+ const sharedSetup = sharedAppInstalled || sharedAppWithoutBot
112116 const searchPolicies = useSearchIntegrations ( organizationId ?? '' , {
113- enabled : open && sharedAppInstalled ,
117+ enabled : open && sharedSetup ,
114118 } )
115119 const searchApproved = searchPolicies . data ?. some (
116120 ( policy ) => policy . connectorType === 'slack' && policy . approved
117121 )
118- const accounts = useOrganizationAccounts ( open && sharedAppInstalled ? organizationId : undefined )
122+ const accounts = useOrganizationAccounts ( open && sharedSetup ? organizationId : undefined )
119123 const memberGroup = accounts . data ?. credentialGroup
120124 const memberOption = memberGroup ?. options . find ( ( option ) => option . provider === 'slack' )
125+ const memberSetupSettled =
126+ accounts . isSuccess &&
127+ ! accounts . isFetching &&
128+ ! accounts . error &&
129+ searchPolicies . isSuccess &&
130+ ! searchPolicies . isFetching &&
131+ ! searchPolicies . error &&
132+ memberGroup ?. id === credentialGroupId
121133 const sharedAppCanAuthorize = Boolean (
122134 sharedAppInstalled &&
123135 apps . isSuccess &&
@@ -126,19 +138,18 @@ export function SlackManagedUsersModal({
126138 apps . data ?. sharedAppAvailable &&
127139 selectedApp . enabled &&
128140 ! selectedApp . needsValidation &&
129- accounts . isSuccess &&
130- ! accounts . isFetching &&
131- ! accounts . error &&
132- searchPolicies . isSuccess &&
133- ! searchPolicies . isFetching &&
134- ! searchPolicies . error &&
135- memberGroup ?. id === credentialGroupId &&
141+ memberSetupSettled &&
136142 memberOption ?. status === 'active'
137143 )
144+ const memberOnlyCanAuthorize =
145+ sharedAppWithoutBot &&
146+ ! apps . isFetching &&
147+ memberSetupSettled &&
148+ ( ! memberOption || memberOption . status === 'active' )
138149 const searchPermissionsMissing =
139150 searchApproved && ! hasSlackSearchUserScopes ( memberOption ?. requiredScopes )
140151 const sharedAppReady =
141- sharedAppCanAuthorize &&
152+ ( sharedAppCanAuthorize || memberOnlyCanAuthorize ) &&
142153 memberOption ?. configurationStatus === 'ready' &&
143154 ! searchPermissionsMissing
144155 const sharedAppNeedsUpdate =
@@ -176,7 +187,7 @@ export function SlackManagedUsersModal({
176187 : 'search' )
177188 const requiredScopes =
178189 scope . kind === 'organization'
179- ? selectedApp
190+ ? selectedApp || sharedAppWithoutBot
180191 ? [ ...SLACK_SEARCH_USER_SCOPES ]
181192 : [ ]
182193 : access === null
@@ -300,15 +311,24 @@ export function SlackManagedUsersModal({
300311 if (
301312 pending ||
302313 ( sharedAppInstalled && ! sharedAppNeedsUpdate ) ||
314+ ( sharedAppWithoutBot && ! memberOnlyCanAuthorize ) ||
303315 ( ! organizationSetup && ! selectedBot )
304316 )
305317 return
306- if (
307- organizationSetup
308- ? ! selectedApp || ! requiredScopes . length
309- : ! clientId . trim ( ) || ! clientSecret . trim ( )
310- )
318+ if ( organizationSetup ? ! requiredScopes . length : ! clientId . trim ( ) || ! clientSecret . trim ( ) )
311319 return
320+ const body = {
321+ ...( organizationSetup
322+ ? selectedApp
323+ ? { appId : selectedApp . appId , teamId : selectedApp . teamId }
324+ : { }
325+ : {
326+ slackBotCredentialId : selectedBot ?. id ,
327+ clientId : clientId . trim ( ) ,
328+ clientSecret : clientSecret . trim ( ) ,
329+ } ) ,
330+ requiredScopes,
331+ }
312332
313333 if ( isDesktopApp ( ) ) {
314334 const controller = new AbortController ( )
@@ -320,16 +340,7 @@ export function SlackManagedUsersModal({
320340 kind : 'slack-managed-users' ,
321341 owner : resourceScopeFields ( scope ) ,
322342 credentialGroupId,
323- body : {
324- ...( organizationSetup
325- ? { appId : selectedApp ?. appId , teamId : selectedApp ?. teamId }
326- : {
327- slackBotCredentialId : selectedBot ?. id ,
328- clientId : clientId . trim ( ) ,
329- clientSecret : clientSecret . trim ( ) ,
330- } ) ,
331- requiredScopes,
332- } ,
343+ body,
333344 } ,
334345 controller . signal
335346 )
@@ -365,16 +376,7 @@ export function SlackManagedUsersModal({
365376 const result = await startAuthorization . mutateAsync ( {
366377 ...resourceScopeFields ( scope ) ,
367378 credentialGroupId,
368- body : {
369- ...( organizationSetup
370- ? { appId : selectedApp ?. appId , teamId : selectedApp ?. teamId }
371- : {
372- slackBotCredentialId : selectedBot ?. id ,
373- clientId : clientId . trim ( ) ,
374- clientSecret : clientSecret . trim ( ) ,
375- } ) ,
376- requiredScopes,
377- } ,
379+ body,
378380 } )
379381 if ( popup . current !== opened ) return
380382 expectedState . current = result . state
@@ -403,28 +405,28 @@ export function SlackManagedUsersModal({
403405 if ( sharedAppReady && ! appSetupOpen ) return null
404406
405407 const noBots = ! organizationSetup && ! isLoading && bots . length === 0
406- const needsApp = organizationSetup && apps . isSuccess && availableApps . length === 0
408+ const needsApp = noInstalledApp && ! sharedAppWithoutBot
407409 const checkingSetup =
408410 apps . isPending ||
409- ( sharedAppInstalled &&
411+ ( sharedSetup &&
410412 ( apps . isFetching ||
411413 accounts . isPending ||
412414 accounts . isFetching ||
413415 searchPolicies . isPending ||
414416 searchPolicies . isFetching ) )
415417 const failedSetup = apps . error
416418 ? apps
417- : sharedAppInstalled && searchPolicies . error
419+ : sharedSetup && searchPolicies . error
418420 ? searchPolicies
419- : sharedAppInstalled && accounts . error
421+ : sharedSetup && accounts . error
420422 ? accounts
421423 : null
422424 const title = organizationSetup ? 'Set up Slack app' : 'Set up Slack'
423425 const primaryLabel = isLoading
424426 ? 'Loading...'
425427 : pending
426428 ? 'Waiting for Slack...'
427- : sharedAppNeedsUpdate
429+ : sharedAppNeedsUpdate || ( sharedAppWithoutBot && memberOption )
428430 ? 'Update member access'
429431 : 'Verify and add'
430432 const primaryDisabled =
@@ -433,7 +435,10 @@ export function SlackManagedUsersModal({
433435 ( ! organizationSetup && ! selectedBot ) ||
434436 pending ||
435437 ( organizationSetup
436- ? checkingSetup || Boolean ( failedSetup ) || ! selectedApp || ! requiredScopes . length
438+ ? checkingSetup ||
439+ Boolean ( failedSetup ) ||
440+ ( sharedAppWithoutBot && ! memberOnlyCanAuthorize ) ||
441+ ! requiredScopes . length
437442 : ! clientId . trim ( ) || ! clientSecret . trim ( ) )
438443
439444 return (
@@ -465,6 +470,14 @@ export function SlackManagedUsersModal({
465470 Retry
466471 </ Chip >
467472 </ ChipModalField >
473+ ) : sharedAppWithoutBot ? (
474+ < ChipModalField type = 'custom' title = 'Member accounts' >
475+ < p className = 'text-[var(--text-secondary)] text-sm' >
476+ { memberOption
477+ ? 'Verify member permissions. Members will need to reconnect if their app or permissions change.'
478+ : 'Verify member permissions in your Slack workspace.' }
479+ </ p >
480+ </ ChipModalField >
468481 ) : needsApp ? (
469482 < ChipModalField type = 'custom' title = 'Install Sim Search first' >
470483 < p className = 'text-[var(--text-secondary)] text-sm' >
0 commit comments