Skip to content

Commit bbc4fb7

Browse files
committed
improvement(org): show an account's sites and confirm connections in the organization view
#9762 added both to the legacy integration page, which the organization view replaced: an account lists the sites its grant reaches (a count when several accounts are listed), and a finished connect or reconnect says so. Both data layers carry sites and take an onSuccess for a completed connection. The Slack custom-bot Request URL helper moves to lib/integrations/slack-manifest, so its callers no longer load the block registry.
1 parent d76de99 commit bbc4fb7

15 files changed

Lines changed: 75 additions & 26 deletions

File tree

‎apps/sim/app/workspace/[workspaceId]/integrations/components/connect-slack-bot-modal/connect-slack-bot-modal.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { resourceScopeFields, resourceScopeFromOwner } from '@/lib/core/resource
2020
import { getBaseUrl } from '@/lib/core/utils/urls'
2121
import {
2222
buildSlackAppCreationUrl,
23+
buildSlackCustomBotRequestUrl,
2324
getSlackAppNameError,
2425
SLACK_APP_CREATION_URL_MAX_LENGTH,
2526
} from '@/lib/integrations/slack-manifest'
@@ -35,7 +36,6 @@ import {
3536
SLACK_MANAGED_USER_AUTHORIZATION_CAPABILITY,
3637
type SlackSlashCommand,
3738
} from '@/triggers/slack/capabilities'
38-
import { buildSlackCustomBotRequestUrl } from '@/triggers/webhook-url'
3939

4040
const logger = createLogger('ConnectSlackBotModal')
4141

‎apps/sim/lib/integrations/slack-manifest.ts‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { getBaseUrl } from '@/lib/core/utils/urls'
2+
13
const SLACK_APP_NAME_MAX_LENGTH = 35
24
export const SLACK_APP_CREATION_URL_MAX_LENGTH = 30_000
35

@@ -6,6 +8,16 @@ export function buildSlackAppCreationUrl(manifest: string): string {
68
return `https://api.slack.com/apps?new_app=1&manifest_json=${encodeURIComponent(manifest)}`
79
}
810

11+
/**
12+
* The Request URL a Slack custom-bot app posts events to. One URL per
13+
* credential (not per workflow): the endpoint verifies with the credential's
14+
* signing secret and fans out to every workflow whose trigger routes by this
15+
* credential id. Uses the app's public base so Slack's servers can reach it.
16+
*/
17+
export function buildSlackCustomBotRequestUrl(credentialId: string): string {
18+
return `${getBaseUrl()}/api/webhooks/slack/custom/${credentialId}`
19+
}
20+
921
/** Validates a name used for both the Slack app and its bot user. */
1022
export function getSlackAppNameError(name: string): string | null {
1123
const trimmedName = name.trim()

‎apps/sim/lib/mothership/tools/handlers/connect-slack-bot.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { createLogger } from '@sim/logger'
22
import { createServiceAccountCredentialUseCase } from '@/lib/credentials/application/service-account'
3+
import { buildSlackCustomBotRequestUrl } from '@/lib/integrations/slack-manifest'
34
import { messageForCopilotApplicationError } from '@/lib/mothership/application/errors'
45
import { executeCopilotCredentialUseCase } from '@/lib/mothership/application/execute-credential-use-case'
56
import { ConnectSlackBotInputSchema } from '@/lib/mothership/generated/protocol'
67
import type { ToolHandler } from '@/lib/mothership/tool-executor/types'
78
import { requireCopilotWorkspace } from '@/lib/mothership/tools/server/workspace-scope'
8-
import { buildSlackCustomBotRequestUrl } from '@/triggers/webhook-url'
99

1010
const logger = createLogger('ConnectSlackBot')
1111

‎apps/sim/lib/webhooks/deployed-urls.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest'
55
vi.mock('@/lib/webhooks/trigger-url', () => ({
66
buildWebhookTriggerUrl: (path: string) => `https://sim.test/api/webhooks/trigger/${path}`,
77
}))
8-
vi.mock('@/triggers/webhook-url', () => ({
8+
vi.mock('@/lib/integrations/slack-manifest', () => ({
99
buildSlackCustomBotRequestUrl: (credentialId: string) =>
1010
`https://sim.test/api/webhooks/slack/custom/${credentialId}`,
1111
}))

‎apps/sim/lib/webhooks/deployed-urls.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import { webhook } from '@sim/db/schema'
33
import { isRecordLike } from '@sim/utils/object'
44
import { and, eq, isNull } from 'drizzle-orm'
55
import type { DbOrTx } from '@/lib/db/types'
6+
import { buildSlackCustomBotRequestUrl } from '@/lib/integrations/slack-manifest'
67
import { LEGACY_SLACK_CUSTOM_BOT_INGRESS_MODE } from '@/lib/webhooks/slack-custom-ingress-constants'
78
import { buildWebhookTriggerUrl } from '@/lib/webhooks/trigger-url'
8-
import { buildSlackCustomBotRequestUrl } from '@/triggers/webhook-url'
99

1010
/** The public URL one live webhook registration receives events on, and the block it feeds. */
1111
export interface DeployedWebhookUrl {

‎apps/sim/platform/embedded/app/(shell)/integrations/_components/connected-credential/connected-credential.module.css‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@
1414
justify-content: space-between;
1515
gap: var(--ui-row-gap);
1616
}
17+
.sites {
18+
display: grid;
19+
gap: var(--ui-stack-gap);
20+
margin: 0;
21+
padding: 0;
22+
list-style: none;
23+
}
1724
.muted {
1825
color: var(--ui-secondary);
1926
}

‎apps/sim/platform/embedded/app/(shell)/integrations/_components/connected-credential/connected-credential.tsx‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,16 @@ export function ConnectedCredential({
5151
<p className={styles.muted} data-type='T050'>
5252
{account.statusLabel}
5353
</p>
54+
{accounts.length === 1 && account.sites.length > 0 && (
55+
<ul aria-label={`Sites ${account.label} reaches`} className={styles.sites}>
56+
{account.sites.map((site) => (
57+
<li key={site.url} data-type='T050'>
58+
{site.name}{' '}
59+
<span className={styles.muted}>{site.url.replace(/^https:\/\//, '')}</span>
60+
</li>
61+
))}
62+
</ul>
63+
)}
5464
</div>
5565
<div className={styles.actions}>
5666
{account.status === 'needs_reauth' && account.canReconnect && (

‎apps/sim/platform/embedded/app/(shell)/integrations/_components/integration-details/integration-details.tsx‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,10 @@ export function IntegrationDetails({
7575
integrationName={name}
7676
accounts={shown.map((account) => ({
7777
...account,
78-
statusLabel: accountStatus(integration, account),
78+
statusLabel:
79+
shown.length > 1 && account.sites.length > 0
80+
? `${accountStatus(integration, account)} · ${account.sites.length} ${account.sites.length === 1 ? 'site' : 'sites'}`
81+
: accountStatus(integration, account),
7982
href: !selected && account.hasToolPermissions ? accountHref(account.id) : undefined,
8083
}))}
8184
pending={pending}

‎apps/sim/platform/embedded/app/(shell)/integrations/_components/integrations-page/integrations-route.tsx‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
} from '@/platform/embedded/lib/data/integrations'
3333
import { integrationPath } from '@/platform/embedded/lib/data/integrations-model'
3434
import { GENERIC_SECRETS_SEGMENT } from '@/platform/shared/integrations'
35+
import { toast } from '@/platform/shared/toast'
3536

3637
const LOADING_CONNECTIONS = 'Loading your connections'
3738
const LOADING_CONNECTION = 'Loading connection'
@@ -209,9 +210,16 @@ export function IntegrationsRoute({
209210
}
210211
pending={actions.pending}
211212
onConnect={() => {
212-
if (integration.connectionTarget) actions.connect(integration.connectionTarget)
213+
if (integration.connectionTarget)
214+
actions.connect(integration.connectionTarget, {
215+
onSuccess: () => toast.success(`${integration.name} connected`),
216+
})
213217
}}
214-
onReconnect={actions.reconnect}
218+
onReconnect={(credentialId) =>
219+
actions.reconnect(credentialId, {
220+
onSuccess: () => toast.success(`${integration.name} reconnected`),
221+
})
222+
}
215223
onDisconnect={async (credentialId) => {
216224
await actions.disconnect(credentialId)
217225
if (account === credentialId) void setParams({ account: null, tools: null })

‎apps/sim/platform/embedded/lib/data/integrations-model.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ function toOrganizationIntegration(entry: MemberIntegration): OrganizationIntegr
6464
status: account.status,
6565
canReconnect: entry.reconnectAccountIds.includes(account.credentialId),
6666
hasToolPermissions: entry.permissionAccountIds.includes(account.credentialId),
67+
sites: 'sites' in account ? account.sites : [],
6768
})),
6869
}
6970
}

0 commit comments

Comments
 (0)