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>
130 lines
4.9 KiB
TypeScript
130 lines
4.9 KiB
TypeScript
import type { Category, Partner } from '~/types/partners'
|
|
import type { CatalogListing } from 'common/marketplace-client'
|
|
import Image from 'next/image'
|
|
import Link from 'next/link'
|
|
|
|
export default function TileGrid({
|
|
partners,
|
|
hideCategories = false,
|
|
}: {
|
|
partners: Partner[]
|
|
hideCategories?: boolean
|
|
}) {
|
|
const partnersByCategory: { [slug: string]: { category: Category; partners: Partner[] } } = {}
|
|
partners.forEach((p) =>
|
|
// TODO: This will result in duplicate listings if a partner appears in multiple categories. Consider choosing just one.
|
|
p.categories.forEach((category) => {
|
|
if (!partnersByCategory[category.slug]) {
|
|
partnersByCategory[category.slug] = { category, partners: [] }
|
|
}
|
|
partnersByCategory[category.slug].partners.push(p)
|
|
})
|
|
)
|
|
|
|
const featuredPartners = partners
|
|
.filter((p) => p.featured)
|
|
.toSorted((a, b) => a.title.localeCompare(b.title))
|
|
|
|
return (
|
|
<>
|
|
{featuredPartners.length > 0 ? (
|
|
<div
|
|
key="featured"
|
|
id="featured"
|
|
className={`space-y-8 ${hideCategories ? `pb-8 border-b mb-8` : ''}`}
|
|
>
|
|
<h2 className="h2">Featured</h2>
|
|
<div className="grid grid-cols-1 gap-5 lg:max-w-none lg:grid-cols-2 xl:grid-cols-3">
|
|
{featuredPartners?.map((p) => (
|
|
<Link key={p.slug} href={`/partners/catalog/${p.slug}`}>
|
|
<div
|
|
className="
|
|
bg-surface-100
|
|
hover:bg-surface-200
|
|
group flex h-full w-full flex-col rounded-xl border px-6
|
|
py-6 shadow-sm
|
|
transition-all
|
|
hover:shadow-lg"
|
|
>
|
|
<div className="flex w-full space-x-6">
|
|
<div className="relative h-[40px] min-w-[40px] w-[40px] rounded-full overflow-hidden scale-100 transition-all group-hover:scale-110">
|
|
<Image
|
|
layout="fill"
|
|
objectFit="cover"
|
|
className="bg-surface-100"
|
|
src={p.logo}
|
|
alt={p.title}
|
|
/>
|
|
</div>
|
|
<div>
|
|
<h3 className="text-foreground-light group-hover:text-foreground mb-2 text-xl transition-colors">
|
|
{p.title}
|
|
</h3>
|
|
<p
|
|
className="text-foreground-lighter text-sm line-clamp-4 min-h-[80px]"
|
|
title={p.description}
|
|
>
|
|
{p.description}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
</div>
|
|
) : null}
|
|
{Object.keys(partnersByCategory)
|
|
.toSorted()
|
|
.map((slug) => {
|
|
const partners = partnersByCategory[slug].partners.toSorted((a, b) =>
|
|
a.title.localeCompare(b.title)
|
|
)
|
|
return (
|
|
<div key={slug} id={slug} className="space-y-8">
|
|
{!hideCategories && <h2 className="h2">{partnersByCategory[slug].category.name}</h2>}
|
|
<div className="grid grid-cols-1 gap-5 lg:max-w-none lg:grid-cols-2 xl:grid-cols-3">
|
|
{partners.map((p) => (
|
|
<Link key={p.slug} href={`/partners/catalog/${p.slug}`}>
|
|
<div
|
|
className="
|
|
bg-surface-100
|
|
hover:bg-surface-200
|
|
group flex h-full w-full flex-col rounded-xl border px-6
|
|
py-6 shadow-sm
|
|
transition-all
|
|
hover:shadow-lg"
|
|
>
|
|
<div className="flex w-full space-x-6">
|
|
<div className="relative h-[40px] min-w-[40px] w-[40px] rounded-full overflow-hidden scale-100 transition-all group-hover:scale-110">
|
|
<Image
|
|
layout="fill"
|
|
objectFit="cover"
|
|
className="bg-surface-100"
|
|
src={p.logo}
|
|
alt={p.title}
|
|
/>
|
|
</div>
|
|
<div>
|
|
<h3 className="text-light group-hover:text-foreground mb-2 text-xl transition-colors">
|
|
{p.title}
|
|
</h3>
|
|
<p
|
|
className="text-foreground-lighter text-sm line-clamp-4 min-h-[80px]"
|
|
title={p.description}
|
|
>
|
|
{p.description}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)
|
|
})}
|
|
</>
|
|
)
|
|
}
|