Skip to content

Commit b5890cf

Browse files
committed
Merge commit '8b719d2c430d7634e00abb694951dcd4950f777e' into wip/platform-org-wiring
2 parents d7e26a1 + 8b719d2 commit b5890cf

8 files changed

Lines changed: 45 additions & 34 deletions

File tree

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

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ export function IntegrationDetails({ integration, onChange }: IntegrationDetails
4040
const canConnect = !account && integration.availability === 'ready' && connectableMethod !== null
4141
const canAuthorize = Boolean(integration.fixtureAuthorization)
4242
const canAttach = canAuthorize && canConnect && connectionCandidate?.method === connectableMethod
43+
const accessMessage =
44+
integration.availability === 'managed'
45+
? `Managed by your organization. ${integration.accessDescription}`
46+
: integration.accessDescription
4347

4448
function connect() {
4549
if (!canAttach || !connectionCandidate) return
@@ -102,26 +106,21 @@ export function IntegrationDetails({ integration, onChange }: IntegrationDetails
102106
title='This credential is no longer connected'
103107
action={<Chip href={providerHref}>View integration</Chip>}
104108
/>
105-
) : canConnect ? (
109+
) : !account ? (
106110
<ConnectionPrompt
107-
title={`Connect your ${integration.name} account`}
111+
title={canConnect ? `Connect your ${integration.name} account` : accessMessage}
108112
setupGuideUrl={integration.setupGuideUrl}
109-
options={[
110-
{
111-
label: integration.name,
112-
icon: { providerId },
113-
onConnect: canAttach ? connect : undefined,
114-
},
115-
]}
116-
/>
117-
) : !account ? (
118-
<EmptyState
119-
title={
120-
integration.availability === 'managed'
121-
? 'Organization managed'
122-
: 'No connected credential'
113+
options={
114+
canConnect
115+
? [
116+
{
117+
label: integration.name,
118+
icon: { providerId },
119+
onConnect: canAttach ? connect : undefined,
120+
},
121+
]
122+
: []
123123
}
124-
description={integration.accessDescription}
125124
/>
126125
) : null}
127126
{!staleCredential && account?.permissions && (

‎apps/sim/platform/app/(shell)/integrations/_components/member-secrets/member-secrets.tsx‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { EmptyState } from '@/platform/design-system/components'
2+
import { ConnectionPrompt } from '@/platform/design-system/components/connection-prompt/connection-prompt'
23
import {
34
SettingsRow,
45
SettingsSection,
@@ -12,12 +13,11 @@ interface MemberSecretsProps {
1213
export function MemberSecrets({ source }: MemberSecretsProps) {
1314
if (source.mode === 'organization') {
1415
return (
15-
<SettingsSection title='Access'>
16-
<SettingsRow
17-
label='Organization managed'
18-
description='Your organization’s administrators manage these credentials.'
19-
/>
20-
</SettingsSection>
16+
<ConnectionPrompt
17+
title='Your organization’s administrators manage these credentials.'
18+
setupGuideUrl={source.setupGuideUrl}
19+
options={[]}
20+
/>
2121
)
2222
}
2323
if (source.names.length === 0) return <EmptyState title='No personal secrets' />

‎apps/sim/platform/checks/integrations.spec.ts‎

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,17 @@ test('managed, unconfigured and retained connections expose only the actions the
102102
await expect(reads).toContainText('Read available meeting summaries')
103103
await expect(main.getByRole('radio')).toHaveCount(0)
104104
await main.getByRole('link', { name: 'Integrations', exact: true }).click()
105-
await main.getByRole('link', { name: /^GitLab / }).click()
106-
await expect(main.getByRole('heading', { name: 'Organization managed' })).toBeVisible()
107-
await expect(main.getByRole('button', { name: 'Connect account' })).toHaveCount(0)
108-
await main.getByRole('link', { name: 'Integrations', exact: true }).click()
105+
const managed = main.getByRole('region', { name: 'Managed by your organization' })
106+
for (const provider of ['GitLab', 'Slack', 'Granola', 'Zoom']) {
107+
await managed.getByRole('link', { name: new RegExp(`^${provider} `) }).click()
108+
const docs = main.getByRole('link', { name: /Docs/ })
109+
await expect(docs).toHaveAttribute(
110+
'href',
111+
`https://docs.sim.ai/search/${provider.toLowerCase()}`
112+
)
113+
await expect(main.getByRole('button', { name: provider, exact: true })).toHaveCount(0)
114+
await main.getByRole('link', { name: 'Integrations', exact: true }).click()
115+
}
109116
await main.getByRole('link', { name: /^Confluence / }).click()
110117
await main.getByRole('button', { name: 'Manage engineering@sim.ai' }).click()
111118
await expect(page.getByRole('menuitem', { name: 'Disconnect account' })).toBeVisible()

‎apps/sim/platform/checks/member-secrets.spec.ts‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,12 @@ test('the member secrets destination delivers only the credential names the view
2020
expect(await managed?.text()).not.toContain('REPORTING_TOKEN')
2121
await link.click()
2222
await expect(page).toHaveURL(/\/integrations\/secrets\?.*secrets=organization/)
23-
await expect(
24-
page.getByRole('main').getByText('Organization managed', { exact: true })
25-
).toBeVisible()
23+
const main = page.getByRole('main')
24+
await expect(main.getByRole('link', { name: /Docs/ })).toHaveAttribute(
25+
'href',
26+
'https://docs.sim.ai/search/generic-secrets'
27+
)
28+
await expect(main.getByRole('button')).toHaveCount(0)
2629
const detail = await page.reload()
2730
expect(await detail?.text()).not.toContain('REPORTING_TOKEN')
2831
})

‎apps/sim/platform/docs/organization-integrations.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ The existing [organization route](../../app/o/%5BorganizationId%5D/integrations/
1313

1414
With `SIM_PLATFORM_FIXTURES=1`, open [the populated inventory](http://localhost:3100/integrations?state=full), [the empty state](http://localhost:3100/integrations?state=empty), or [the stress scenario](http://localhost:3100/integrations?state=stress). The flagged loader supplies sample credential handoffs and tool catalogs. Changes live in the current product session. The default-off route has no example providers or credentials. Real authorization, credential exchange, policy persistence and secret handling belong to Phase 3.
1515

16-
The detail header places the provider mark beside its name and description. **Connected credential** is a separate section below the header. Its title uses the Home section heading typography and aligns with the left edge of the permissions sections. The member’s account label sits on the next line. Its trailing menu acts on that credential. A status appears when the account needs reconnection or its organization has restricted access. In the inline connection prompt, the book icon and **Docs** link sit to the left of the connect action and open the provider’s Sim Search setup guide in a new tab. Providers without a dedicated guide use the member account connection guide.
16+
The detail header places the provider mark beside its name and description. **Connected credential** is a separate section below the header. Its title uses the Home section heading typography and aligns with the left edge of the permissions sections. The member’s account label sits on the next line. Its trailing menu acts on that credential. A status appears when the account needs reconnection or its organization has restricted access. In the inline connection prompt, the book icon and **Docs** link sit to the left of the connect action and open the provider’s Sim Search setup guide in a new tab. For entries under **Managed by your organization**, the same panel explains the access state and shows only **Docs**. Organization-managed Generic Secrets links to its own guide; personal secret names stay private to the member view. Providers without a dedicated guide use the member account connection guide.
1717

1818
Provider names and descriptions come from Sim’s workspace integration catalog, which is generated from block definitions. Lucid uses its managed connector definition. **Tool permissions** groups eligible actions by read and write. Each tool retains its own description and approval control. **Requested permissions** lists human-readable OAuth scopes in always-visible groups. These are authorization requests. **Search access** describes the content a member can search. For managed providers such as Fireflies it lists the read-only Search operations the existing adapter supports. Use hanging bullets for static scopes and Search capabilities so wrapped lines stay with their item. Editable tool rows keep the name, description and permission control together without bullets. Actual account grants and eligible assistant tools come from their own operations. Refresh canonical metadata with `bun scripts/sync-platform-integration-metadata.ts` when its sources change.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export type MemberSecretSource = { name: string; description: string } & (
1+
export type MemberSecretSource = { name: string; description: string; setupGuideUrl: string } & (
22
| { mode: 'organization' }
33
| { mode: 'member'; names: readonly string[] }
44
)

‎apps/sim/platform/lib/integrations/metadata.generated.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,4 +444,5 @@ export const gmailToolMetadata = [
444444
export const memberSecretMetadata = {
445445
name: 'Generic Secrets',
446446
description: 'Store shared or personal secrets for organization Home conversations',
447+
setupGuideUrl: 'https://docs.sim.ai/search/generic-secrets',
447448
} as const

‎scripts/sync-platform-integration-metadata.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,9 @@ const gmailTools = (GmailV2Block.tools?.access ?? []).flatMap((id) => {
168168
? [{ id, label: tool.name, description: tool.description, kind: tool.assistant.effect }]
169169
: []
170170
})
171+
const secretGuide = 'generic-secrets'
171172
const { title, description } = matter.read(
172-
resolve(root, 'apps/docs/content/docs/search/generic-secrets.mdx')
173+
resolve(root, `apps/docs/content/docs/search/${secretGuide}.mdx`)
173174
).data
174175
if (typeof title !== 'string' || typeof description !== 'string')
175176
throw new Error('Generic Secrets needs a name and description in its Search guide')
@@ -178,7 +179,7 @@ const generated = formatGeneratedSource(
178179
`/** Generated by scripts/sync-platform-integration-metadata.ts. */
179180
export const providerMetadata = ${JSON.stringify(providers)} as const
180181
export const gmailToolMetadata = ${JSON.stringify(gmailTools)} as const
181-
export const memberSecretMetadata = ${JSON.stringify({ name: title, description })} as const
182+
export const memberSecretMetadata = ${JSON.stringify({ name: title, description, setupGuideUrl: `${SIM_DOCS_URL}/search/${secretGuide}` })} as const
182183
`,
183184
output,
184185
root

0 commit comments

Comments
 (0)