Skip to content

Commit bdad787

Browse files
committed
fix(platform): restore the example credential page's tool permissions and instance labels
The example Permissions tab rendered through the live permissions owner after the consolidation, which brought group controls, Read-only tools and the live notices onto Emir's example page. It renders his own example component and stylesheet again. Example credentials name their host or environment with his labels; a live credential still names it from the connection the catalog describes.
1 parent 7692c7e commit bdad787

5 files changed

Lines changed: 164 additions & 50 deletions

File tree

‎apps/sim/app/(product)/o/[organizationId]/integrations/_components/credential-detail/credential-detail.tsx‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,9 +152,14 @@ export function CredentialDetail({
152152
viewOnly={!editable}
153153
optional
154154
/>
155-
{credential.instanceUrl && credential.instanceLabel && (
155+
{credential.instanceUrl && (
156156
<TextField
157-
label={credential.instanceLabel}
157+
label={
158+
credential.instanceLabel ??
159+
(credential.providerId === 'microsoft-dataverse'
160+
? 'Environment URL'
161+
: 'GitLab instance')
162+
}
158163
placeholder='Connection URL'
159164
value={credential.instanceUrl}
160165
viewOnly

‎apps/sim/app/(product)/o/[organizationId]/integrations/_components/fixtures/integration-details/integration-details.tsx‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,9 +242,6 @@ export function IntegrationDetails({
242242
description: draft.description,
243243
accountLabel: draft.accountLabel,
244244
instanceUrl: draft.instanceUrl,
245-
instanceLabel: enrollment.connection.fields.find(
246-
(field) => field.id === 'domain' || field.id === 'dataverseEnvironmentUrl'
247-
)?.label,
248245
role: 'admin',
249246
status: 'active',
250247
canReconnect: true,
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
.section,
2+
.group,
3+
.list,
4+
.intro,
5+
.copy {
6+
display: grid;
7+
min-inline-size: 0;
8+
}
9+
.section {
10+
gap: var(--ui-section-gap);
11+
container: permission-tools / inline-size;
12+
}
13+
.intro,
14+
.copy {
15+
gap: var(--ui-stack-gap);
16+
}
17+
.detail {
18+
color: var(--ui-secondary);
19+
}
20+
.group {
21+
gap: var(--ui-stack-gap);
22+
}
23+
.tool {
24+
display: flex;
25+
align-items: center;
26+
justify-content: space-between;
27+
gap: var(--ui-row-gap);
28+
min-inline-size: 0;
29+
}
30+
.list {
31+
gap: var(--ui-list-gap);
32+
}
33+
.tool {
34+
padding-block: var(--ui-row-block);
35+
}
36+
.actions {
37+
display: flex;
38+
flex-wrap: wrap;
39+
align-items: center;
40+
justify-content: end;
41+
gap: var(--ui-action-gap);
42+
max-inline-size: 100%;
43+
}
44+
.copy {
45+
flex: 1;
46+
overflow-wrap: anywhere;
47+
}
48+
@container permission-tools (max-width: 32rem) {
49+
.tool {
50+
align-items: start;
51+
flex-direction: column;
52+
}
53+
}
Lines changed: 104 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,129 @@
11
'use client'
22

33
import { useState } from 'react'
4-
import { ToolPermissions } from '@/app/(product)/o/[organizationId]/integrations/_components/tool-permissions/tool-permissions'
4+
import { EmptyState } from '@sim/emcn/empty-state'
5+
import { CountedLabel } from '@sim/emcn/overflow-text/counted-label'
6+
import { SearchField } from '@sim/emcn/search-field'
7+
import { SegmentedControl } from '@sim/emcn/segmented-control'
8+
import { ControlBar } from '@/app/(product)/_components/control-bar/control-bar'
9+
import styles from '@/app/(product)/o/[organizationId]/integrations/_components/fixtures/tool-permissions/tool-permissions.module.css'
510
import type {
611
CredentialToolPermissions,
712
IntegrationCredential,
813
} from '@/platform/lib/integrations/credential-model'
9-
import type { ToolPermissionChange, ToolPolicy } from '@/platform/shared/integrations'
14+
import type { ToolPolicy } from '@/platform/shared/integrations'
15+
16+
const ACTIONS = [
17+
{ value: 'allow', label: 'Allow' },
18+
{ value: 'ask', label: 'Ask' },
19+
{ value: 'block', label: 'Block' },
20+
] as const
21+
const GROUPS = [
22+
{ kind: 'read', label: 'Read tools' },
23+
{ kind: 'write', label: 'Write tools' },
24+
] as const
1025

1126
interface FixtureToolPermissionsProps {
1227
account: Pick<IntegrationCredential, 'status'>
1328
permissions: CredentialToolPermissions
1429
onChange: (next: CredentialToolPermissions) => void
15-
readOnly: boolean
30+
readOnly?: boolean
1631
}
1732

18-
/** Example tool policies, edited in the product session through the live permissions owner. */
33+
/** Example tool policies, edited in the product session. */
1934
export function FixtureToolPermissions({
2035
account,
2136
permissions,
2237
onChange,
2338
readOnly,
2439
}: FixtureToolPermissionsProps) {
2540
const [query, setQuery] = useState('')
26-
const override = (kind: 'read' | 'write', policy: ToolPolicy | null) =>
27-
policy === permissions[kind] ? null : policy
28-
function apply(change: ToolPermissionChange) {
29-
if (change.kind === 'group')
30-
onChange({
31-
...permissions,
32-
[change.effect]: change.policy,
33-
tools: permissions.tools.map((tool) =>
34-
tool.kind === change.effect ? { ...tool, override: null } : tool
35-
),
36-
})
37-
else
38-
onChange({
39-
...permissions,
40-
tools: permissions.tools.map((tool) =>
41-
tool.id === change.toolId
42-
? { ...tool, override: override(tool.kind, change.policy) }
43-
: tool
44-
),
45-
})
41+
const canAllow = permissions.canAutoApprove && account.status === 'active'
42+
const choices = ACTIONS.map((option) => ({
43+
...option,
44+
disabled: readOnly || (option.value === 'allow' && !canAllow),
45+
}))
46+
const filter = query.trim().toLowerCase()
47+
const matches = permissions.tools.filter((tool) =>
48+
`${tool.label} ${tool.description ?? ''}`.toLowerCase().includes(filter)
49+
)
50+
51+
function updateTool(id: string, value: ToolPolicy, defaultValue: ToolPolicy) {
52+
onChange({
53+
...permissions,
54+
tools: permissions.tools.map((tool) =>
55+
tool.id === id ? { ...tool, override: value === defaultValue ? null : value } : tool
56+
),
57+
})
4658
}
59+
4760
return (
48-
<ToolPermissions
49-
permissions={{
50-
read: permissions.read,
51-
write: permissions.write,
52-
canAutoApprove: permissions.canAutoApprove,
53-
accountActive: account.status === 'active',
54-
tools: permissions.tools.map((tool) => ({
55-
id: tool.id,
56-
label: tool.label,
57-
description: tool.description ?? '',
58-
kind: tool.kind,
59-
policy: tool.override ?? permissions[tool.kind],
60-
inherited: tool.override === null,
61-
})),
62-
}}
63-
query={query}
64-
onQueryChange={setQuery}
65-
pending={readOnly}
66-
onChange={apply}
67-
/>
61+
<section className={styles.section} aria-label='Tool permissions'>
62+
<div className={styles.intro}>
63+
<h2 data-type='T093'>Tool permissions</h2>
64+
{!canAllow && (
65+
<p className={styles.detail} data-type='T060'>
66+
{account.status === 'needs_reauth'
67+
? 'Reconnect this account to allow tools automatically.'
68+
: 'Your organization requires approval before tools can run.'}
69+
</p>
70+
)}
71+
</div>
72+
<ControlBar
73+
search={
74+
<SearchField
75+
variant='quiet'
76+
label='Search tools'
77+
placeholder='Search tools…'
78+
onValueChange={setQuery}
79+
/>
80+
}
81+
/>
82+
{GROUPS.map(({ kind, label }) => {
83+
const matching = matches.filter((tool) => tool.kind === kind)
84+
if (!matching.length) return null
85+
const tools = permissions.tools.filter((item) => item.kind === kind)
86+
const defaultValue = permissions[kind]
87+
const defaultLabel =
88+
ACTIONS.find((action) => action.value === defaultValue)?.label ?? defaultValue
89+
return (
90+
<section key={kind} className={styles.group} aria-label={label}>
91+
<h3 data-type='T059'>
92+
<CountedLabel label={label} count={tools.length} />
93+
</h3>
94+
<ul className={styles.list}>
95+
{matching.map((tool) => (
96+
<li className={styles.tool} key={tool.id}>
97+
<div className={styles.copy}>
98+
<span data-type='T059'>{tool.label}</span>
99+
{tool.description && (
100+
<span className={styles.detail} data-type='T060'>
101+
{tool.description}
102+
</span>
103+
)}
104+
</div>
105+
<div className={styles.actions}>
106+
<SegmentedControl
107+
label={`${tool.label} permission`}
108+
description={
109+
tool.override
110+
? `Choose ${defaultLabel} to inherit the default for ${label.toLowerCase()}.`
111+
: `Inherits ${defaultLabel} from ${label.toLowerCase()}.`
112+
}
113+
options={choices}
114+
value={tool.override ?? defaultValue}
115+
onSelect={(change) => updateTool(tool.id, change, defaultValue)}
116+
/>
117+
</div>
118+
</li>
119+
))}
120+
</ul>
121+
</section>
122+
)
123+
})}
124+
{!matches.length && (
125+
<EmptyState title={filter ? 'No matching tools' : 'No tools available'} />
126+
)}
127+
</section>
68128
)
69129
}

‎apps/sim/platform/fixtures/integration-data.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,6 @@ function credentialExamples(
251251
displayName: 'My GitLab account',
252252
description: '',
253253
instanceUrl: 'https://gitlab.com',
254-
instanceLabel: 'GitLab host',
255254
role: 'admin',
256255
status: 'active',
257256
canReconnect: true,

0 commit comments

Comments
 (0)