Files
Ivan VasilovandJoshen Lim 05a542ccea chore: Migrate all feather icons to lucide icons (#29038)
* Add lucide-react to docs (to make the autocomplete work).

* Migrate the docs app icons.

* Migrate the ui-patterns.

* Remove the old icons from ui package.

* Migrate the www app from react-feather icons.

* Migrate all of studio icons.

* Migrate the only component in design-system.

* Fix an old import in ui package. Revert an import in docs app.

* Fix some pages in www.

* Remove unneeded files used in generation of icons.

* Fix a prettier error.

* Fix more issues in www.

* Fix an issue in Log Date picker.

* Replace all string sizes with number sizes because the icons grew in some cases.

* Fix more imports in security page.

* Fix an extra import.

* Remove the size prop from all icons if they're in a button and they match the button size.

* Minor fixes for docs and www.

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2024-09-04 19:46:21 +08:00

219 lines
7.3 KiB
TypeScript

import { Loader, Search } from 'lucide-react'
import { NextSeo } from 'next-seo'
import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'
import { Input } from 'ui'
import { useDebounce } from 'use-debounce'
import DefaultLayout from '~/components/Layouts/Default'
import SectionContainer from '~/components/Layouts/SectionContainer'
import BecomeAPartner from '~/components/Partners/BecomeAPartner'
import PartnerLinkBox from '~/components/Partners/PartnerLinkBox'
import supabase from '~/lib/supabaseMisc'
import type { Partner } from '~/types/partners'
import TileGrid from '../../../components/Partners/TileGrid'
export async function getStaticProps() {
const { data: partners } = await supabase
.from('partners')
.select('*')
.eq('approved', true)
.eq('type', 'technology')
.order('category')
.order('title')
return {
props: {
partners,
},
// TODO: consider using Next.js' On-demand Revalidation with Supabase Database Webhooks instead
revalidate: 1800, // 30 minutes
}
}
interface Props {
partners: Partner[]
}
function IntegrationPartnersPage(props: Props) {
const initialPartners = props.partners ?? []
const [partners, setPartners] = useState(initialPartners)
const allCategories = Array.from(new Set(initialPartners?.map((p) => p.category)))
const router = useRouter()
const meta_title = 'Find an Integration'
const meta_description = `Use your favorite tools with Supabase.`
const [search, setSearch] = useState('')
const [debouncedSearchTerm] = useDebounce(search, 300)
const [isSearching, setIsSearching] = useState(false)
useEffect(() => {
const searchPartners = async () => {
setIsSearching(true)
let query = supabase
.from('partners')
.select('*')
.eq('approved', true)
.order('category')
.order('title')
if (search.trim()) {
query = query.textSearch('tsv', `${search.trim()}`, {
type: 'websearch',
config: 'english',
})
}
const { data: partners } = await query
return partners
}
if (search.trim() === '') {
setIsSearching(false)
setPartners(initialPartners)
return
}
searchPartners().then((partners) => {
if (partners) {
setPartners(partners)
}
setIsSearching(false)
})
}, [debouncedSearchTerm, router])
return (
<>
<NextSeo
title={meta_title}
description={meta_description}
openGraph={{
title: meta_title,
description: meta_description,
url: `https://supabase.com/partners/integrations`,
images: [
{
url: `https://supabase.com${router.basePath}/images/og/integrations.png`, // TODO
},
],
}}
/>
<DefaultLayout className="bg-alternative">
<SectionContainer className="space-y-16">
<div>
<h1 className="h1">{meta_title}</h1>
<p className="text-foreground-lighter text-xl">{meta_description}</p>
</div>
{/* Title */}
<div className="grid space-y-12 md:gap-8 lg:grid-cols-12 lg:gap-16 lg:space-y-0 xl:gap-16">
<div className="lg:col-span-4 xl:col-span-3">
{/* Horizontal link menu */}
<div className="space-y-6">
{/* Search Bar */}
<Input
size="small"
icon={<Search />}
placeholder="Search..."
type="text"
value={search}
onChange={(e) => setSearch(e.target.value)}
actions={
isSearching && (
<span className="mr-1 animate-spin text-white">
<Loader />
</span>
)
}
/>
<div className="hidden lg:block">
<div className="text-foreground-lighter mb-2 text-sm">Categories</div>
<div className="space-y-1">
{allCategories.map((category) => (
<button
key={category}
onClick={() => router.push(`#${category.toLowerCase()}`)}
className="text-foreground-light block text-base"
>
{category}
</button>
))}
</div>
</div>
<div className="space-y-4">
<div className="text-foreground-lighter mb-2 text-sm">Explore more</div>
<div className="grid grid-cols-2 gap-8 lg:grid-cols-1">
<PartnerLinkBox
title="Experts"
color="blue"
description="Explore our certified Supabase agency experts that build with Supabase"
href={`/partners/experts`}
icon={
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth="1"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M21 13.255A23.931 23.931 0 0112 15c-3.183 0-6.22-.62-9-1.745M16 6V4a2 2 0 00-2-2h-4a2 2 0 00-2 2v2m4 6h.01M5 20h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
/>
</svg>
}
/>
<PartnerLinkBox
href={`/partners/integrations#become-a-partner`}
title="Become a partner"
color="brand"
description="Fill out a quick 30 second form to apply to become a partner"
icon={
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth="1"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"
/>
</svg>
}
/>
</div>
</div>
</div>
</div>
<div className="lg:col-span-8 xl:col-span-9">
{/* Partner Tiles */}
<div className="grid space-y-10">
{partners?.length ? (
<TileGrid partners={partners} />
) : (
<p className="h2">No Partners Found</p>
)}
</div>
</div>
</div>
</SectionContainer>
<BecomeAPartner />
</DefaultLayout>
</>
)
}
export default IntegrationPartnersPage