mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 01:45:10 +03:00
## Info architecture change around "Partners" The www "integrations" now become more partner-driven. `/partners/integrations` -> now Partner Catalog under `/partners/catalog` (old links redirect to new paths) Moved them close together in the nav dropdown and in the footer <img width="494" height="336" alt="Screenshot 2026-07-09 at 11 06 41" src="https://github.com/user-attachments/assets/a875fef0-0ab8-47ca-8756-d658b27c4892" /> <img width="1149" height="665" alt="Screenshot 2026-07-09 at 11 09 48" src="https://github.com/user-attachments/assets/9631bb72-fe25-4fb4-b1af-9f14a37d02e7" /> ## /partners This page remains untouched in this PR, updates to layout, content and intake form are delegated to #47874 ## /partners/catalog Listed in the [catalog](https://zone-www-dot-com-git-feat-www-partners-pages-supabase.vercel.app/partners/catalog) are now partners. Some partners match with a listing. <img width="1207" height="866" alt="Screenshot 2026-07-09 at 11 14 17" src="https://github.com/user-attachments/assets/b65216be-976f-4ef5-91f8-1ad49da87b45" /> ## /partners/catalog/[partner] Each partner can have one or more "listings" which are either - simple guides - foreign data wrappers - dashboard integrations Integrations available in the dashboard now all have a prominent "Install integration" cta to open it in the dashboard [integrations page](https://supabase.com/dashboard/project/_/integrations). <img width="1269" height="776" alt="Screenshot 2026-07-09 at 11 16 51" src="https://github.com/user-attachments/assets/3c7bb715-ffce-4d0a-905f-9a660c3b1f5a" /> ## Docs Update docs → [Preview](https://docs-git-feat-www-partners-pages-supabase.vercel.app/docs/guides/integrations) - remove "Supabase marketplace" - use "Dashboard Integrations and Partner Catalog - update integrations in sidenav to link to updated /partners/catalog/** listings <img width="1520" height="696" alt="Screenshot 2026-07-15 at 12 54 47" src="https://github.com/user-attachments/assets/9f5a2794-4536-4299-97df-9732d3d75b4c" /> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a Partner Catalog experience with search, category filters, official-partner toggle, responsive filtering (sidebar + bottom sheet), grid/list views, and featured partners. * Added Partner Catalog detail pages with tabbed listings, MDX-rendered content, image gallery with zoom overlay, and “add/install” actions. * **Improvements** * Updated “Become a Partner” layout and form support for prefilled values and checkbox-group fields (including validation). * Updated navigation/footer/docs and partner tile links to use Partner Catalog routes; expanded redirects from legacy integrations paths. * Added public agent-skills discovery manifest. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Alan Daniel <stylesshjs@gmail.com> Co-authored-by: Alex Hall <alex.hall@supabase.io> Co-authored-by: Miranda Limonczenko <miranda.limonczenko@supabase.io>
103 lines
3.8 KiB
TypeScript
103 lines
3.8 KiB
TypeScript
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 <Layout additionalNavItems={additionalNavItems}>{children}</Layout>
|
|
}
|
|
|
|
// 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<string>()
|
|
const partnerNameBySlug = new Map<string, string>()
|
|
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<NavMenuSection>[] = []
|
|
const listingsByPartnerSlug = new Map<string, CatalogListing[]>()
|
|
|
|
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<NavMenuSection>[] = [
|
|
...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
|
|
}
|