Unified · Split
| @@ -37,6 +37,7 @@ import { IntegrationEditSheet } from "../components/metadata-edit-sheet"; · 36 unmodified lines | ||
| 37 | 37 | import { Tabs, TabsContent, TabsList, TabsTrigger } from "../components/tabs"; |
| 38 | 38 | import { authMethodsFromDescriptors, type AuthMethod } from "../lib/auth-placements"; |
| 39 | 39 | import { usePolicyActions } from "../hooks/use-policy-actions"; |
| 40 | +import { useCanCreateWorkspaceConnections } from "../multiplayer/use-admin-nav"; | |
| 40 | 41 | import { useIntegrationPlugins, type IntegrationAccountHandoff } from "@executor-js/sdk/client"; |
| 41 | 42 | import { Button } from "../components/button"; |
| 42 | 43 | import { Skeleton } from "../components/skeleton"; |
| @@ -84,9 +85,13 @@ export function IntegrationDetailPage(props: { · 41 unmodified lines | ||
| 84 | 85 | const connectionsResult = useAtomValue(connectionsAllAtom); |
| 85 | 86 | const refreshIntegrations = useAtomRefresh(integrationsOptimisticAtom); |
| 86 | 87 | const refreshTools = useAtomRefresh(integrationToolsAllAtom(slug)); |
| 87 | − const doRemove = useAtomSet(removeIntegrationOptimistic, { mode: "promiseExit" }); | |
| 88 | + const doRemove = useAtomSet(removeIntegrationOptimistic, { | |
| 89 | + mode: "promiseExit", | |
| 90 | + }); | |
| 88 | 91 | const doRefresh = useAtomSet(refreshConnection, { mode: "promiseExit" }); |
| 89 | − const doCheckHealth = useAtomSet(checkConnectionHealth, { mode: "promiseExit" }); | |
| 92 | + const doCheckHealth = useAtomSet(checkConnectionHealth, { | |
| 93 | + mode: "promiseExit", | |
| 94 | + }); | |
| 90 | 95 | // Policies are owner-partitioned on write; the integration policy menu writes |
| 91 | 96 | // Workspace (org) rules, preserving the prior default behavior. |
| 92 | 97 | const policyActions = usePolicyActions("org"); |
| @@ -133,8 +138,12 @@ export function IntegrationDetailPage(props: { · 40 unmodified lines | ||
| 133 | 138 | useExecutorDocumentTitle(integrationData?.name || namespace); |
| 134 | 139 | const isBuiltInIntegration = namespace === "executor" || integrationData?.kind === "built-in"; |
| 135 | 140 | 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; | |
| 136 | 145 | const canRefresh = integrationData?.canRefresh ?? false; |
| 137 | − const canRemove = integrationData?.canRemove ?? false; | |
| 146 | + const canRemove = canMutateIntegration && (integrationData?.canRemove ?? false); | |
| 138 | 147 | const urlAccountHandoff = useMemo<IntegrationAccountHandoff | null>(() => { |
| 139 | 148 | const search = new URLSearchParams(locationSearch); |
| 140 | 149 | // The route-validated flag and the raw `addAccount=1` are the same request; |
| @@ -389,7 +398,11 @@ export function IntegrationDetailPage(props: { · 248 unmodified lines | ||
| 389 | 398 | if (connection.integration !== slug) continue; |
| 390 | 399 | connectionCount++; |
| 391 | 400 | 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 | + }, | |
| 393 | 406 | reactivityKeys: connectionWriteKeys, |
| 394 | 407 | }); |
| 395 | 408 | refreshExits.push(Exit.isSuccess(refreshExit)); |
| @@ -485,7 +498,7 @@ export function IntegrationDetailPage(props: { · 89 unmodified lines | ||
| 485 | 498 | </div> |
| 486 | 499 | |
| 487 | 500 | <div className="flex shrink-0 items-center gap-2"> |
| 488 | − {!confirmDelete && !isBuiltInIntegration && integrationData && ( | |
| 501 | + {!confirmDelete && canEdit && ( | |
| 489 | 502 | <Button variant="outline" size="sm" onClick={() => setEditSheetOpen(true)}> |
| 490 | 503 | Edit |
| 491 | 504 | </Button> |