@@ -37,6 +37,7 @@ import { IntegrationEditSheet } from "../components/metadata-edit-sheet";
3737import { Tabs , TabsContent , TabsList , TabsTrigger } from "../components/tabs" ;
3838import { authMethodsFromDescriptors , type AuthMethod } from "../lib/auth-placements" ;
3939import { usePolicyActions } from "../hooks/use-policy-actions" ;
40+ import { useCanCreateWorkspaceConnections } from "../multiplayer/use-admin-nav" ;
4041import { useIntegrationPlugins , type IntegrationAccountHandoff } from "@executor-js/sdk/client" ;
4142import { Button } from "../components/button" ;
4243import { Skeleton } from "../components/skeleton" ;
@@ -84,9 +85,13 @@ export function IntegrationDetailPage(props: {
8485 const connectionsResult = useAtomValue ( connectionsAllAtom ) ;
8586 const refreshIntegrations = useAtomRefresh ( integrationsOptimisticAtom ) ;
8687 const refreshTools = useAtomRefresh ( integrationToolsAllAtom ( slug ) ) ;
87- const doRemove = useAtomSet ( removeIntegrationOptimistic , { mode : "promiseExit" } ) ;
88+ const doRemove = useAtomSet ( removeIntegrationOptimistic , {
89+ mode : "promiseExit" ,
90+ } ) ;
8891 const doRefresh = useAtomSet ( refreshConnection , { mode : "promiseExit" } ) ;
89- const doCheckHealth = useAtomSet ( checkConnectionHealth , { mode : "promiseExit" } ) ;
92+ const doCheckHealth = useAtomSet ( checkConnectionHealth , {
93+ mode : "promiseExit" ,
94+ } ) ;
9095 // Policies are owner-partitioned on write; the integration policy menu writes
9196 // Workspace (org) rules, preserving the prior default behavior.
9297 const policyActions = usePolicyActions ( "org" ) ;
@@ -133,8 +138,12 @@ export function IntegrationDetailPage(props: {
133138 useExecutorDocumentTitle ( integrationData ?. name || namespace ) ;
134139 const isBuiltInIntegration = namespace === "executor" || integrationData ?. kind === "built-in" ;
135140 const currentTab = isBuiltInIntegration ? "tools" : activeTab ;
141+ // Integrations are workspace-owned; the server refuses catalog mutations
142+ // (update/remove) from non-admin members, so hide the controls for them.
143+ const canMutateIntegration = useCanCreateWorkspaceConnections ( ) ;
144+ const canEdit = canMutateIntegration && ! isBuiltInIntegration && integrationData !== null ;
136145 const canRefresh = integrationData ?. canRefresh ?? false ;
137- const canRemove = integrationData ?. canRemove ?? false ;
146+ const canRemove = canMutateIntegration && ( integrationData ?. canRemove ?? false ) ;
138147 const urlAccountHandoff = useMemo < IntegrationAccountHandoff | null > ( ( ) => {
139148 const search = new URLSearchParams ( locationSearch ) ;
140149 // The route-validated flag and the raw `addAccount=1` are the same request;
@@ -389,7 +398,11 @@ export function IntegrationDetailPage(props: {
389398 if ( connection . integration !== slug ) continue ;
390399 connectionCount ++ ;
391400 const refreshExit = await doRefresh ( {
392- params : { owner : connection . owner , integration : slug , name : connection . name } ,
401+ params : {
402+ owner : connection . owner ,
403+ integration : slug ,
404+ name : connection . name ,
405+ } ,
393406 reactivityKeys : connectionWriteKeys ,
394407 } ) ;
395408 refreshExits . push ( Exit . isSuccess ( refreshExit ) ) ;
@@ -485,7 +498,7 @@ export function IntegrationDetailPage(props: {
485498 </ div >
486499
487500 < div className = "flex shrink-0 items-center gap-2" >
488- { ! confirmDelete && ! isBuiltInIntegration && integrationData && (
501+ { ! confirmDelete && canEdit && (
489502 < Button variant = "outline" size = "sm" onClick = { ( ) => setEditSheetOpen ( true ) } >
490503 Edit
491504 </ Button >
0 commit comments