Skip to content

Commit e453d01

Browse files
committed
Simplify integration headers and group member permissions
1 parent 1603c64 commit e453d01

17 files changed

Lines changed: 382 additions & 156 deletions

File tree

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,25 @@
1+
.section,
12
.scopes,
23
.scope {
34
display: grid;
45
min-inline-size: 0;
56
}
6-
.scopes {
7+
.section {
78
gap: var(--ui-section-gap);
89
}
10+
.header {
11+
display: flex;
12+
flex-wrap: wrap;
13+
align-items: center;
14+
justify-content: space-between;
15+
gap: var(--ui-row-gap);
16+
}
17+
.scopes {
18+
gap: var(--ui-list-gap);
19+
margin-block-start: var(--ui-row-gap);
20+
}
921
.scope {
1022
gap: var(--ui-stack-gap);
1123
overflow-wrap: anywhere;
12-
}
13-
.scope > span + span {
1424
color: var(--ui-secondary);
1525
}
Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,51 @@
1+
'use client'
2+
3+
import { useState } from 'react'
14
import styles from '@/platform/app/(shell)/integrations/_components/connection-access/connection-access.module.css'
2-
import { Disclosure } from '@/platform/design-system/components'
5+
import {
6+
SCOPE_GROUPS,
7+
scopeGroup,
8+
} from '@/platform/app/(shell)/integrations/_components/connection-access/scope-groups'
9+
import { Button, Disclosure } from '@/platform/design-system/components'
310
import type { OrganizationIntegration } from '@/platform/lib/integrations/model'
411

512
interface ConnectionAccessProps {
613
integration: OrganizationIntegration
714
}
815

916
export function ConnectionAccess({ integration }: ConnectionAccessProps) {
17+
const [showIds, setShowIds] = useState(false)
1018
const scopes = integration.requestedScopes
1119
if (!scopes?.length) return null
1220
return (
13-
<Disclosure label='Requested permissions' variant='activity'>
14-
<ul className={styles.scopes} aria-label='Requested provider permissions'>
15-
{scopes.map(({ id, label }) => (
16-
<li key={id} className={styles.scope}>
17-
<span data-type='T059'>{label}</span>
18-
{label !== id && <span data-type='T050'>{id}</span>}
19-
</li>
20-
))}
21-
</ul>
22-
</Disclosure>
21+
<section className={styles.section} aria-label='Requested permissions'>
22+
<header className={styles.header}>
23+
<h2 data-type='T093'>Requested permissions</h2>
24+
<Button
25+
size='compact'
26+
variant='quiet'
27+
render={<button type='button' aria-expanded={showIds} />}
28+
onClick={() => setShowIds(!showIds)}
29+
>
30+
{showIds ? 'Hide scope IDs' : 'Show scope IDs'}
31+
</Button>
32+
</header>
33+
{SCOPE_GROUPS.map((group) => {
34+
const entries = scopes.filter(({ id }) => scopeGroup(id) === group)
35+
if (!entries.length) return null
36+
return (
37+
<Disclosure key={group} label={group} initiallyOpen>
38+
<ul className={styles.scopes} aria-label={group}>
39+
{entries.map(({ id, label }) => (
40+
<li key={id} className={styles.scope}>
41+
<span data-type='T060'>{label}</span>
42+
{showIds && label !== id && <span data-type='T050'>{id}</span>}
43+
</li>
44+
))}
45+
</ul>
46+
</Disclosure>
47+
)
48+
})}
49+
</section>
2350
)
2451
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
const ACCOUNT_SCOPES = new Set([
2+
'openid',
3+
'userinfo.email',
4+
'userinfo.profile',
5+
'read:me',
6+
'offline_access',
7+
'refresh_token',
8+
])
9+
10+
const GOOGLE_WRITE_SCOPES = new Set([
11+
'gmail.send',
12+
'gmail.modify',
13+
'gmail.labels',
14+
'calendar',
15+
'drive',
16+
'drive.file',
17+
'meetings.space.created',
18+
])
19+
20+
export const SCOPE_GROUPS = [
21+
'Account and sign-in',
22+
'Read and search',
23+
'Write and manage',
24+
'Provider access',
25+
] as const
26+
27+
export function scopeGroup(id: string): (typeof SCOPE_GROUPS)[number] {
28+
const scope = id.replace(/^https:\/\/www\.googleapis\.com\/auth\//, '')
29+
if (ACCOUNT_SCOPES.has(scope)) return 'Account and sign-in'
30+
if (GOOGLE_WRITE_SCOPES.has(scope) || /^(write|delete|manage)(:|$)/.test(scope))
31+
return 'Write and manage'
32+
if (/^(read|search)(:|$)|:(read|history)([.:]|$)|\.readonly$/.test(scope))
33+
return 'Read and search'
34+
return 'Provider access'
35+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
'use client'
2+
3+
import { useState } from 'react'
4+
import { ActionMenu, Modal } from '@/platform/design-system/components'
5+
import type { IntegrationAccount } from '@/platform/lib/integrations/model'
6+
7+
interface CredentialActionsProps {
8+
account: IntegrationAccount
9+
canAuthorize: boolean
10+
onReconnect: () => void
11+
onDisconnect: () => void
12+
}
13+
14+
export function CredentialActions({
15+
account,
16+
canAuthorize,
17+
onReconnect,
18+
onDisconnect,
19+
}: CredentialActionsProps) {
20+
const [confirming, setConfirming] = useState(false)
21+
return (
22+
<>
23+
<ActionMenu
24+
label={`Manage ${account.label}`}
25+
icon='I108'
26+
triggerSize='row'
27+
align='end'
28+
tooltip={false}
29+
actions={[
30+
...(account.canReconnect
31+
? [
32+
{
33+
id: 'reconnect',
34+
label: 'Reconnect account',
35+
disabled: !canAuthorize,
36+
onSelect: onReconnect,
37+
},
38+
]
39+
: []),
40+
{
41+
id: 'disconnect',
42+
label: 'Disconnect account',
43+
destructive: true,
44+
separatorBefore: account.canReconnect,
45+
onSelect: () => setConfirming(true),
46+
},
47+
]}
48+
/>
49+
{confirming && (
50+
<Modal
51+
open
52+
onOpenChange={setConfirming}
53+
title='Disconnect account?'
54+
variant='confirm'
55+
destructive
56+
description={`Sim will lose access to ${account.label}.`}
57+
confirmLabel='Disconnect'
58+
onConfirm={onDisconnect}
59+
/>
60+
)}
61+
</>
62+
)
63+
}

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

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,11 @@
1-
.sections,
21
.account,
32
.list {
43
display: grid;
54
min-inline-size: 0;
65
}
7-
.sections {
8-
gap: var(--ui-product-section-gap);
9-
}
106
.account {
117
gap: var(--ui-section-gap);
128
}
13-
.header {
14-
display: flex;
15-
flex-wrap: wrap;
16-
align-items: center;
17-
justify-content: space-between;
18-
gap: var(--ui-row-gap);
19-
}
209
.list {
2110
gap: var(--ui-list-gap);
2211
margin-inline: calc(-1 * var(--ui-row-inset));

0 commit comments

Comments
 (0)