import { type NavMenuSection } from '~/components/Navigation/Navigation.types' import { REVALIDATION_TAGS } from '~/features/helpers.fetch' import Layout from '~/layouts/guides' import { IS_PLATFORM } from 'common' import { createMarketplaceClient, SUPABASE_LISTING_OVERRIDES, SUPABASE_PARTNER_SLUG, type CatalogListing, } from 'common/marketplace-client' import { unstable_cache } from 'next/cache' export default async function IntegrationsLayout({ children }: { children: React.ReactNode }) { const additionalNavItems = { integrations: await getPartners() } return {children} } // Will need to turn on revalidation later, just turning it off for now so we // can slowly turn things back on while monitoring usage const getPartners = unstable_cache(getPartnersImpl, [], { tags: [REVALIDATION_TAGS.PARTNERS], }) async function getPartnersImpl() { if (!IS_PLATFORM) return [] const marketplaceClient = createMarketplaceClient() const [{ data: partners, error }, { data: listings }] = await Promise.all([ marketplaceClient.from('partners').select('slug, name, type'), marketplaceClient.from('catalog_listings').select('*'), ]) if (error) { console.error(new Error('Error fetching partners', { cause: error })) } // Only "technology" partners belong in this list, not agencies/experts. const technologyPartnerSlugs = new Set() const partnerNameBySlug = new Map() for (const partner of partners ?? []) { if (!partner.slug) continue partnerNameBySlug.set(partner.slug, partner.name ?? partner.slug) if (partner.type === 'technology') technologyPartnerSlugs.add(partner.slug) } // Supabase-owned listings still get their own nav item, at their clean catalog page slug. const supabaseOwnedNavItems: Partial[] = [] const listingsByPartnerSlug = new Map() for (const listing of listings ?? []) { if (!listing.slug) continue const override = SUPABASE_LISTING_OVERRIDES[listing.slug] if (override) { supabaseOwnedNavItems.push({ name: listing.title || override.name, url: `https://supabase.com/partners/catalog/${override.slug}` as `https://${string}`, }) continue } const partnerSlug = listing.partner_slug if ( !partnerSlug || partnerSlug === SUPABASE_PARTNER_SLUG || !technologyPartnerSlugs.has(partnerSlug) ) { continue } const partnerListings = listingsByPartnerSlug.get(partnerSlug) ?? [] partnerListings.push(listing) listingsByPartnerSlug.set(partnerSlug, partnerListings) } // One nav item per listing (a partner with multiple listings — e.g. an FDW plus a // Dashboard Integration — gets one entry per listing), linking straight to the tab // that listing occupies on its Partner Catalog page. const partnerNavItems: Partial[] = [ ...Array.from(listingsByPartnerSlug.entries()).flatMap(([partnerSlug, partnerListings]) => { const partnerName = partnerNameBySlug.get(partnerSlug) ?? partnerSlug const catalogUrl = `https://supabase.com/partners/catalog/${partnerSlug}` // Show only listing name if partner has one listing if (partnerListings.length === 1) { const listingName = partnerListings[0].title || partnerName return [{ name: listingName, url: catalogUrl as `https://${string}` }] } // otherwise show listing name (or slug if no title) plus partner name. return partnerListings.map((listing) => ({ name: `${listing.title || listing.slug} (${partnerName})`, url: `${catalogUrl}?tab=${listing.slug}` as `https://${string}`, })) }), ...supabaseOwnedNavItems, ] partnerNavItems.sort((a, b) => (a.name ?? '').localeCompare(b.name ?? '')) return partnerNavItems }