From 5ce086b06d2abd88582b8684dcf4f76bf62e9683 Mon Sep 17 00:00:00 2001 From: Alex Hall Date: Thu, 11 Jun 2026 15:30:56 -0400 Subject: [PATCH] feat(studio): Marketplace integration partner links (#46827) Adds partner-provided links for installed marketplace integrations (if provided) to the marketplace detail breadcrumbs --- .../Landing/useIntegrationDetail.ts | 11 ++++++ .../Marketplace/MarketplaceDetail.tsx | 36 +++++++++++++++++-- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/apps/studio/components/interfaces/Integrations/Landing/useIntegrationDetail.ts b/apps/studio/components/interfaces/Integrations/Landing/useIntegrationDetail.ts index f9d8e5cb46d..ec54eaff6c1 100644 --- a/apps/studio/components/interfaces/Integrations/Landing/useIntegrationDetail.ts +++ b/apps/studio/components/interfaces/Integrations/Landing/useIntegrationDetail.ts @@ -2,6 +2,7 @@ import { useParams } from 'common' import { useRouter } from 'next/router' import { useEffect, useMemo } from 'react' +import { useProjectOAuthIntegrationData } from './Landing.utils' import { useAvailableIntegrations } from './useAvailableIntegrations' import { useInstalledIntegrations } from './useInstalledIntegrations' import { @@ -44,6 +45,14 @@ export const useIntegrationDetail = () => { [installedIntegrations, id] ) + // There does exist a mgmt-api endpoint to get a single status by integration-id, but the bulk results are likely already cached so go ahead and use them. + const { data: projectData, isLoading: isIntegrationStatusLoading } = + useProjectOAuthIntegrationData(project?.ref, { enabled: integration?.type === 'oauth' }) + const integrationStatus = useMemo( + () => projectData.partnerIntegrations.find((i) => i.listing_slug === id), + [projectData, id] + ) + const isInstalled = !!integration && !!installation const areRequiredExtensionsInstalled = useMemo( @@ -140,12 +149,14 @@ export const useIntegrationDetail = () => { integrationsWrappers, integration, installation, + integrationStatus, isInstalled, areRequiredExtensionsInstalled, installActionType, wrappersTabHref, isAvailableLoading, isInstalledLoading, + isIntegrationStatusLoading, Component, } } diff --git a/apps/studio/components/interfaces/Integrations/Marketplace/MarketplaceDetail.tsx b/apps/studio/components/interfaces/Integrations/Marketplace/MarketplaceDetail.tsx index df2ff449db8..c2510a85921 100644 --- a/apps/studio/components/interfaces/Integrations/Marketplace/MarketplaceDetail.tsx +++ b/apps/studio/components/interfaces/Integrations/Marketplace/MarketplaceDetail.tsx @@ -1,4 +1,4 @@ -import { ArrowUpRight, BookOpen } from 'lucide-react' +import { ArrowUpRight, BookOpen, Gauge, Settings } from 'lucide-react' import { useRouter } from 'next/router' import { Button, cn } from 'ui' import { GenericSkeletonLoader, ShimmeringLoader } from 'ui-patterns' @@ -29,18 +29,20 @@ export const MarketplaceDetail = () => { pageTitle, pageSubTitle, integration, + integrationStatus, isInstalled, installActionType, wrappersTabHref, isAvailableLoading, isInstalledLoading, + isIntegrationStatusLoading, Component, } = useIntegrationDetail() if (!isReady) return null if (isWrapperBlocked) return - if (isAvailableLoading || isInstalledLoading) { + if (isAvailableLoading || isInstalledLoading || isIntegrationStatusLoading) { return ( <> @@ -106,6 +108,36 @@ export const MarketplaceDetail = () => { isInstalled={isInstalled} actions={ <> + {isInstalled && integrationStatus?.partner_links?.dashboard && ( + + )} + {isInstalled && integrationStatus?.partner_links?.manage && ( + + )} {integration.docsUrl && (