mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 01:45:10 +03:00
Fix www mobile (#29640)
* fix ent quotes logos * mobile hp product cards * mobile customers section * mobile examples * update sitemap * mobile customers slider * remove autoplay
This commit is contained in:
1 parent
9686f90628
commit
c08a241c69
24 files changed
+492
-262
No files matched your search
@@ -1,49 +0,0 @@
|
||||
import { Button, IconGitHubSolid } from 'ui'
|
||||
import Link from 'next/link'
|
||||
|
||||
import SectionContainer from '~/components/Layouts/SectionContainer'
|
||||
import ExampleCard from './ExampleCard'
|
||||
|
||||
import Examples from 'data/Examples'
|
||||
|
||||
const BuiltWithSupabase = () => {
|
||||
return (
|
||||
<SectionContainer id="examples" className="xl:pt-32">
|
||||
<div className="text-center">
|
||||
<h3 className="h2">Start building in seconds</h3>
|
||||
<p className="p">
|
||||
Kickstart your next project with templates built by us and our community.
|
||||
</p>
|
||||
<div className="flex justify-center gap-2 py-4">
|
||||
<Button asChild type="default" size="small" className="h-full">
|
||||
<Link href="/docs/guides/examples">View all examples</Link>
|
||||
</Button>
|
||||
<Button
|
||||
asChild
|
||||
type="default"
|
||||
icon={<IconGitHubSolid size="tiny" className="!w-full !h-full" />}
|
||||
size="small"
|
||||
>
|
||||
<Link href="https://github.com/supabase/supabase/tree/master/examples">
|
||||
Official GitHub library
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-16 grid grid-cols-12 gap-5">
|
||||
{Examples.slice(0, 6).map((example, i) => {
|
||||
return (
|
||||
<div
|
||||
className={`col-span-12 lg:col-span-6 xl:col-span-4 ${i > 2 && `sm:hidden lg:block`}`}
|
||||
key={i}
|
||||
>
|
||||
<ExampleCard {...example} showProducts />
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</SectionContainer>
|
||||
)
|
||||
}
|
||||
|
||||
export default BuiltWithSupabase
|
||||
@@ -0,0 +1,53 @@
|
||||
import 'swiper/css'
|
||||
|
||||
import React, { FC } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Swiper, SwiperSlide } from 'swiper/react'
|
||||
import ExampleCard from '../ExampleCard'
|
||||
|
||||
import content from '~/data/home/content'
|
||||
import type { Example } from 'data/Examples'
|
||||
|
||||
interface Props {
|
||||
examples: Example[]
|
||||
className?: string
|
||||
}
|
||||
|
||||
const ExamplesMobile: FC<Props> = ({ examples, className }: any) => {
|
||||
const { basePath } = useRouter()
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<Swiper
|
||||
style={{ zIndex: 0, marginRight: '1px' }}
|
||||
initialSlide={0}
|
||||
spaceBetween={12}
|
||||
slidesPerView={1.1}
|
||||
speed={300}
|
||||
watchOverflow
|
||||
threshold={2}
|
||||
updateOnWindowResize
|
||||
className="!px-6 w-full overflow-visible"
|
||||
breakpoints={{
|
||||
320: {
|
||||
slidesPerView: 1.1,
|
||||
},
|
||||
540: {
|
||||
slidesPerView: 1.6,
|
||||
},
|
||||
720: {
|
||||
slidesPerView: 2.5,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{examples.map((example: Example, i: number) => (
|
||||
<SwiperSlide key={`${content}-${i}`}>
|
||||
<ExampleCard {...example} showProducts />
|
||||
</SwiperSlide>
|
||||
))}
|
||||
</Swiper>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ExamplesMobile
|
||||
@@ -0,0 +1,58 @@
|
||||
import { Button, cn, IconGitHubSolid } from 'ui'
|
||||
import Link from 'next/link'
|
||||
|
||||
import SectionContainer from '~/components/Layouts/SectionContainer'
|
||||
import ExampleCard from '../ExampleCard'
|
||||
|
||||
import Examples from 'data/Examples'
|
||||
import ExamplesMobile from './ExamplesMobile'
|
||||
|
||||
const BuiltWithSupabase = () => {
|
||||
return (
|
||||
<>
|
||||
<SectionContainer id="examples" className="xl:pt-32 !pb-0">
|
||||
<div className="text-center flex flex-col items-center">
|
||||
<h3 className="h2">Start building in seconds</h3>
|
||||
<p className="p max-w-[300px] md:max-w-none">
|
||||
Kickstart your next project with templates built by us and our community.
|
||||
</p>
|
||||
<div className="flex justify-center gap-2 py-4">
|
||||
<Button asChild type="default" size="small" className="h-full">
|
||||
<Link href="/docs/guides/examples">View all examples</Link>
|
||||
</Button>
|
||||
<Button
|
||||
asChild
|
||||
type="default"
|
||||
icon={<IconGitHubSolid size="tiny" className="!w-full !h-full" />}
|
||||
size="small"
|
||||
>
|
||||
<Link href="https://github.com/supabase/supabase/tree/master/examples">
|
||||
Official GitHub library
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</SectionContainer>
|
||||
<SectionContainer className="relative w-full !px-0 lg:!px-16 xl:!px-20 !pb-0 mb-16 md:mb-12 lg:mb-12 !pt-6 max-w-[1400px]">
|
||||
<ExamplesMobile examples={Examples.slice(0, 6)} className="lg:hidden" />
|
||||
<div className="hidden lg:grid grid-cols-12 gap-5 mt-4">
|
||||
{Examples.slice(0, 6).map((example, i) => {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'col-span-12 h-full lg:col-span-6 xl:col-span-4',
|
||||
'flex items-stretch'
|
||||
)}
|
||||
key={i}
|
||||
>
|
||||
<ExampleCard {...example} showProducts />
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</SectionContainer>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default BuiltWithSupabase
|
||||
@@ -0,0 +1,48 @@
|
||||
import 'swiper/css'
|
||||
import React from 'react'
|
||||
import { Swiper, SwiperSlide } from 'swiper/react'
|
||||
|
||||
import { cn } from 'ui'
|
||||
import { CompositionCol } from '.'
|
||||
import type { CompositionColType } from '.'
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
columns: CompositionColType[]
|
||||
}
|
||||
|
||||
const CustomersSliderMobile: React.FC<Props> = ({ columns, className }) => (
|
||||
<div className={className}>
|
||||
<Swiper
|
||||
initialSlide={0}
|
||||
spaceBetween={12}
|
||||
slidesPerView="auto"
|
||||
speed={400}
|
||||
watchOverflow
|
||||
threshold={2}
|
||||
updateOnWindowResize
|
||||
allowTouchMove
|
||||
className="!px-6 w-full h-full overflow-visible"
|
||||
>
|
||||
{columns.map((column: CompositionColType, i: number) => (
|
||||
<SwiperSlide
|
||||
className={cn(
|
||||
'flex w-full !h-full',
|
||||
column.type === 'expanded' ? 'w-full max-w-[450px]' : '!w-[250px]'
|
||||
)}
|
||||
key={`${column.cards[0].organization}-mobile-customer-${i}`}
|
||||
>
|
||||
<CompositionCol
|
||||
className={cn(
|
||||
'flex flex-col w-full !h-full gap-3',
|
||||
column.type === 'expanded' ? 'w-full' : 'w-[250px]'
|
||||
)}
|
||||
column={column}
|
||||
/>
|
||||
</SwiperSlide>
|
||||
))}
|
||||
</Swiper>
|
||||
</div>
|
||||
)
|
||||
|
||||
export default CustomersSliderMobile
|
||||
@@ -0,0 +1,52 @@
|
||||
import React from 'react'
|
||||
import { range } from 'lodash'
|
||||
import { cn } from 'ui'
|
||||
|
||||
import { CompositionCol } from '.'
|
||||
import type { CompositionColType } from '.'
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
columns: CompositionColType[]
|
||||
}
|
||||
|
||||
const compositionGap = 'gap-4'
|
||||
|
||||
const CutomsersSliderDesktop: React.FC<Props> = ({ columns, className }) => (
|
||||
<div
|
||||
className={cn(
|
||||
'group/tw-marquee w-full items-stretch h-[300px] min-w-[300px] nowrap mb-16 md:mb-24 lg:mb-24',
|
||||
compositionGap,
|
||||
className
|
||||
)}
|
||||
>
|
||||
{range(0, 2).map((_, idx1: number) => (
|
||||
<div
|
||||
key={`row-${idx1}`}
|
||||
className={cn(
|
||||
'relative',
|
||||
'left-0 z-10',
|
||||
'w-auto h-full',
|
||||
'flex gap-4 items-end',
|
||||
'motion-safe:run motion-safe:animate-[marquee_50000ms_linear_both_infinite] group-hover/tw-marquee:pause',
|
||||
'will-change-transform transition-transform',
|
||||
compositionGap
|
||||
)}
|
||||
>
|
||||
{columns.map((column, idx2) => (
|
||||
<CompositionCol
|
||||
key={`customers-col-${idx1}-${idx2}`}
|
||||
className={cn(
|
||||
'flex flex-col !h-full',
|
||||
compositionGap,
|
||||
column.type === 'expanded' ? 'w-[450px]' : 'w-[250px]'
|
||||
)}
|
||||
column={column}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
|
||||
export default CutomsersSliderDesktop
|
||||
@@ -1,11 +1,11 @@
|
||||
import { PropsWithChildren } from 'react'
|
||||
import dynamic from 'next/dynamic'
|
||||
import Link from 'next/link'
|
||||
import Image from 'next/image'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { range } from 'lodash'
|
||||
import { ArrowRight } from 'lucide-react'
|
||||
import { Button, cn } from 'ui'
|
||||
|
||||
import { Button, cn } from 'ui'
|
||||
import SectionContainer from '~/components/Layouts/SectionContainer'
|
||||
import SectionHeader from 'components/UI/SectionHeader'
|
||||
import Panel from '~/components/Panel'
|
||||
@@ -13,150 +13,124 @@ import Panel from '~/components/Panel'
|
||||
import customerStories from '~/data/CustomerStories'
|
||||
import type { CustomerStoryType } from '~/data/CustomerStories'
|
||||
|
||||
const getCustomer = (customer: string, linked: boolean) => ({
|
||||
...customerStories.find((story: any) => customer.includes(story.organization)),
|
||||
const CustomersSliderMobile = dynamic(() => import('./CustomersSliderMobile'))
|
||||
const CutomsersSliderDesktop = dynamic(() => import('./CutomsersSliderDesktop'))
|
||||
|
||||
const CustomerStories = () => (
|
||||
<div id="customers" className="overflow-hidden pb-16 md:pb-24">
|
||||
<SectionContainer className="!pb-8 w-full flex gap-4 justify-between flex-col xl:flex-row xl:items-end">
|
||||
<SectionHeader
|
||||
title="Infrastructure"
|
||||
title_alt=" to innovate and scale with ease."
|
||||
subtitle="Customer Stories"
|
||||
paragraph="See how Supabase empowers companies of all sizes to accelerate their growth and streamline their work."
|
||||
className="xl:w-1/2"
|
||||
/>
|
||||
<div className="flex gap-2">
|
||||
<Button asChild>
|
||||
<Link href="/customers">View all stories</Link>
|
||||
</Button>
|
||||
<Button asChild type="default">
|
||||
<Link href="/events">View Events</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</SectionContainer>
|
||||
<CustomersSliderMobile columns={compositionCols} className="md:hidden w-full h-[230px]" />
|
||||
<CutomsersSliderDesktop columns={compositionCols} className="hidden md:flex" />
|
||||
</div>
|
||||
)
|
||||
|
||||
/**
|
||||
* Utils & Components
|
||||
*/
|
||||
|
||||
const getCustomer: (customer: string, linked: boolean) => ExtendedCustomerStoryType = (
|
||||
customer,
|
||||
linked
|
||||
) => ({
|
||||
...customerStories.find((story: CustomerStoryType) => customer.includes(story.organization!))!,
|
||||
linked,
|
||||
})
|
||||
|
||||
const CustomerStories = () => {
|
||||
const composition = [
|
||||
{
|
||||
type: 'narrow',
|
||||
cards: [getCustomer('Quivr', false), getCustomer('Tinloof', false)],
|
||||
},
|
||||
{
|
||||
type: 'narrow',
|
||||
cards: [
|
||||
{
|
||||
logo: '/images/customers/logos/1password.png',
|
||||
logo_inverse: '/images/customers/logos/light/1password.png',
|
||||
organization: '1Password',
|
||||
},
|
||||
getCustomer('Next Door Lending', true),
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'expanded',
|
||||
cards: [getCustomer('Maergo', true)],
|
||||
},
|
||||
{
|
||||
type: 'narrow',
|
||||
cards: [
|
||||
getCustomer('Shotgun', true),
|
||||
{
|
||||
logo: '/images/customers/logos/mozilla.png',
|
||||
logo_inverse: '/images/customers/logos/light/mozilla.png',
|
||||
organization: 'Mozilla',
|
||||
linked: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'expanded',
|
||||
cards: [getCustomer('Chatbase', true)],
|
||||
},
|
||||
{
|
||||
type: 'narrow',
|
||||
cards: [getCustomer('Mobbin', true), getCustomer('HappyTeams', true)],
|
||||
},
|
||||
{
|
||||
type: 'expanded',
|
||||
cards: [getCustomer('Pebblely', true)],
|
||||
},
|
||||
]
|
||||
|
||||
const compositionGap = 'gap-4'
|
||||
const compositionCols: CompositionColType[] = [
|
||||
{
|
||||
type: 'narrow',
|
||||
cards: [getCustomer('Quivr', false), getCustomer('Tinloof', false)],
|
||||
},
|
||||
{
|
||||
type: 'narrow',
|
||||
cards: [
|
||||
{
|
||||
logo: '/images/customers/logos/1password.png',
|
||||
logo_inverse: '/images/customers/logos/light/1password.png',
|
||||
organization: '1Password',
|
||||
},
|
||||
getCustomer('Next Door Lending', true),
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'expanded',
|
||||
cards: [getCustomer('Maergo', true)],
|
||||
},
|
||||
{
|
||||
type: 'narrow',
|
||||
cards: [
|
||||
getCustomer('Shotgun', true),
|
||||
{
|
||||
logo: '/images/customers/logos/mozilla.png',
|
||||
logo_inverse: '/images/customers/logos/light/mozilla.png',
|
||||
organization: 'Mozilla',
|
||||
linked: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'expanded',
|
||||
cards: [getCustomer('Chatbase', true)],
|
||||
},
|
||||
{
|
||||
type: 'narrow',
|
||||
cards: [getCustomer('Mobbin', true), getCustomer('HappyTeams', true)],
|
||||
},
|
||||
{
|
||||
type: 'expanded',
|
||||
cards: [getCustomer('Pebblely', true)],
|
||||
},
|
||||
]
|
||||
|
||||
export const CompositionCol: React.FC<CompositionColProps> = ({ column, className }) => {
|
||||
return (
|
||||
<div id="customers" className="overflow-hidden">
|
||||
<SectionContainer className="!pb-8 w-full flex gap-4 justify-between flex-col xl:flex-row xl:items-end">
|
||||
<SectionHeader
|
||||
title={'Infrastructure'}
|
||||
title_alt={' to innovate and scale with ease.'}
|
||||
subtitle={'Customer Stories'}
|
||||
paragraph={
|
||||
'See how Supabase empowers companies of all sizes to accelerate their growth and streamline their work.'
|
||||
}
|
||||
className="xl:w-1/2"
|
||||
/>
|
||||
<div className="flex gap-2">
|
||||
<Button asChild>
|
||||
<Link href="/customers">View all stories</Link>
|
||||
</Button>
|
||||
<Button asChild type="default">
|
||||
<Link href="/events">View Events</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</SectionContainer>
|
||||
<div
|
||||
className={cn(
|
||||
'group/tw-marquee w-full flex items-stretch h-[300px] min-w-[300px] nowrap mb-16 md:mb-24 lg:mb-24',
|
||||
compositionGap
|
||||
)}
|
||||
>
|
||||
{range(0, 2).map((_, idx1: number) => (
|
||||
<div
|
||||
key={`row-${idx1}`}
|
||||
className={cn(
|
||||
'relative',
|
||||
'left-0 z-10',
|
||||
'w-auto h-full',
|
||||
'flex gap-4 items-end',
|
||||
'motion-safe:run motion-safe:animate-[marquee_50000ms_linear_both_infinite] group-hover/tw-marquee:pause',
|
||||
'will-change-transform transition-transform',
|
||||
compositionGap
|
||||
)}
|
||||
<div className={className}>
|
||||
{column.cards.map((customer) =>
|
||||
column.type === 'expanded' ? (
|
||||
<Link
|
||||
href={customer.url!}
|
||||
key={customer.organization}
|
||||
className="col-span-12 md:col-span-4 w-full md:w-[450px] h-full"
|
||||
>
|
||||
{composition.map((group, idx2) => (
|
||||
<div
|
||||
key={`customers-col-${idx1}-${idx2}`}
|
||||
className={cn(
|
||||
'flex flex-col !h-full',
|
||||
compositionGap,
|
||||
group.type === 'expanded' ? 'w-[450px]' : 'w-[250px]'
|
||||
)}
|
||||
>
|
||||
{group.cards.map((customer: any, idx3) =>
|
||||
group.type === 'expanded' ? (
|
||||
<Link
|
||||
href={`${customer.url}`}
|
||||
key={customer.organization}
|
||||
className="col-span-12 md:col-span-4 w-[450px] h-full"
|
||||
>
|
||||
<CustomerCard size="expanded" customer={customer} />
|
||||
</Link>
|
||||
) : customer.linked ? (
|
||||
<Link
|
||||
href={`${customer.url}`}
|
||||
key={customer.organization}
|
||||
className="col-span-12 md:col-span-4 h-full flex-grow"
|
||||
>
|
||||
<CustomerCard size="narrow" customer={customer} />
|
||||
</Link>
|
||||
) : (
|
||||
<CustomerCard
|
||||
size="narrow"
|
||||
key={customer.organization}
|
||||
customer={customer}
|
||||
className="pointer-events-none"
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<CustomerCard size="expanded" customer={customer} />
|
||||
</Link>
|
||||
) : customer.linked ? (
|
||||
<Link
|
||||
href={customer.url!}
|
||||
key={customer.organization}
|
||||
className="col-span-12 md:col-span-4 w-full h-full flex-grow"
|
||||
>
|
||||
<CustomerCard size="narrow" customer={customer} />
|
||||
</Link>
|
||||
) : (
|
||||
<CustomerCard
|
||||
size="narrow"
|
||||
key={customer.organization}
|
||||
customer={customer}
|
||||
className=" pointer-events-none"
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
interface CustomerCardProps extends PropsWithChildren {
|
||||
customer: CustomerStoryType
|
||||
className?: string
|
||||
size?: 'narrow' | 'expanded'
|
||||
}
|
||||
|
||||
const CustomerCard: React.FC<CustomerCardProps> = ({
|
||||
customer,
|
||||
className,
|
||||
@@ -173,7 +147,7 @@ const CustomerCard: React.FC<CustomerCardProps> = ({
|
||||
<div className="relative h-[33px] w-auto max-w-[145px]">
|
||||
<Image
|
||||
src={logoImage}
|
||||
alt={customer.title}
|
||||
alt={customer.title!}
|
||||
fill
|
||||
priority
|
||||
sizes="100%"
|
||||
@@ -188,13 +162,13 @@ const CustomerCard: React.FC<CustomerCardProps> = ({
|
||||
return (
|
||||
<Panel
|
||||
hasActiveOnHover
|
||||
outerClassName={cn('h-full w-[250px] h-full flex-grow', className)}
|
||||
outerClassName={cn('h-full w-full md:w-[250px] h-full flex-grow', className)}
|
||||
innerClassName="flex items-center justify-center"
|
||||
>
|
||||
<Image
|
||||
key={customer.organization}
|
||||
src={customer.logo}
|
||||
alt={customer.organization}
|
||||
src={customer.logo!}
|
||||
alt={customer.organization!}
|
||||
width={300}
|
||||
height={150}
|
||||
priority
|
||||
@@ -208,7 +182,7 @@ const CustomerCard: React.FC<CustomerCardProps> = ({
|
||||
hasActiveOnHover
|
||||
outerClassName={cn(
|
||||
'relative',
|
||||
'h-full',
|
||||
'w-full h-full',
|
||||
'group',
|
||||
'cursor-pointer',
|
||||
'overflow-hidden',
|
||||
@@ -216,13 +190,13 @@ const CustomerCard: React.FC<CustomerCardProps> = ({
|
||||
'transition',
|
||||
className
|
||||
)}
|
||||
innerClassName="h-full p-8 flex flex-col gap-6 justify-between"
|
||||
innerClassName="h-full p-4 md:p-8 flex flex-col gap-6 justify-between"
|
||||
{...rest}
|
||||
>
|
||||
<ArrowRight className="not-sr-only absolute top-8 right-8 -rotate-45 stroke-1 -translate-x-1 translate-y-1 opacity-0 transition-all group-hover:opacity-100 group-hover:translate-x-0 group-hover:translate-y-0" />
|
||||
|
||||
{showLogoInverse && <LogoComponent logoImage={customer.logo_inverse!} />}
|
||||
{showLogo && <LogoComponent logoImage={customer.logo} />}
|
||||
{showLogo && <LogoComponent logoImage={customer.logo!} />}
|
||||
|
||||
<p className="text-base text-foreground-lighter">{customer.title}</p>
|
||||
{children && <span className="text-sm text-foreground-light flex-grow">{children}</span>}
|
||||
@@ -231,4 +205,25 @@ const CustomerCard: React.FC<CustomerCardProps> = ({
|
||||
}
|
||||
}
|
||||
|
||||
/** Types */
|
||||
|
||||
type ColSize = 'narrow' | 'expanded'
|
||||
type ExtendedCustomerStoryType = Partial<CustomerStoryType> & { linked?: boolean }
|
||||
|
||||
export type CompositionColType = {
|
||||
type: ColSize
|
||||
cards: ExtendedCustomerStoryType[]
|
||||
}
|
||||
|
||||
interface CompositionColProps {
|
||||
column: CompositionColType
|
||||
className?: string
|
||||
}
|
||||
|
||||
interface CustomerCardProps extends PropsWithChildren {
|
||||
customer: ExtendedCustomerStoryType
|
||||
className?: string
|
||||
size?: ColSize
|
||||
}
|
||||
|
||||
export default CustomerStories
|
||||
@@ -10,7 +10,9 @@ interface Props {
|
||||
|
||||
const DashboardFeatures: React.FC<Props> = ({ title, tabs }) => (
|
||||
<SectionContainer className="text-center" id="dashboard">
|
||||
<h3 className="mb-8 text-2xl lg:text-4xl text-foreground-lighter">{title}</h3>
|
||||
<h3 className="mb-8 text-2xl md:text-4xl max-w-[300px] sm:max-w-none mx-auto text-foreground-lighter">
|
||||
{title}
|
||||
</h3>
|
||||
<TabsWithHighlights tabs={tabs} />
|
||||
</SectionContainer>
|
||||
)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useBreakpoint } from 'common'
|
||||
import Link from 'next/link'
|
||||
import { ArrowUpRight } from 'lucide-react'
|
||||
import { useBreakpoint } from 'common'
|
||||
|
||||
import { Button } from 'ui'
|
||||
import Panel from './Panel'
|
||||
|
||||
@@ -17,7 +18,7 @@ function ExampleCard(props: any) {
|
||||
|
||||
return (
|
||||
<Link href={props.repo_url} className="w-full h-full" target="_blank">
|
||||
<Panel innerClassName="bg-surface-75 group/panel" hasActiveOnHover>
|
||||
<Panel outerClassName="h-full" innerClassName="bg-surface-75 group/panel" hasActiveOnHover>
|
||||
<div className="flex flex-col justify-between">
|
||||
{props.tags && (
|
||||
<div className="relative w-full aspect-[3/1] bg flex items-center justify-center gap-4 overflow-hidden">
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
import Image from 'next/image'
|
||||
import React from 'react'
|
||||
import { cn } from 'ui'
|
||||
|
||||
const AuthVisual = () => {
|
||||
interface Props {
|
||||
className?: string
|
||||
}
|
||||
|
||||
const AuthVisual: React.FC<Props> = ({ className }) => {
|
||||
return (
|
||||
<figure
|
||||
className="group absolute inset-0 z-0 -top-16 xl:top-0 xl:bottom-0"
|
||||
className={cn('group absolute inset-0 z-0 -top-16 xl:top-0 xl:bottom-0', className)}
|
||||
role="img"
|
||||
aria-label="Supabase Authentication provides Row Level Security which enables you to define custom Policies to restrict access to your database"
|
||||
>
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import React from 'react'
|
||||
import Image from 'next/image'
|
||||
import { range } from 'lodash'
|
||||
import { cn } from 'ui'
|
||||
|
||||
const DataAPIsVisual = () => (
|
||||
<figure className="absolute inset-0 overflow-hidden">
|
||||
interface Props {
|
||||
className?: string
|
||||
}
|
||||
|
||||
const DataAPIsVisual: React.FC<Props> = ({ className }) => (
|
||||
<figure className={cn('absolute inset-0 overflow-hidden', className)}>
|
||||
<div className="absolute z-0 inset-0 flex flex-nowrap">
|
||||
{range(0, 3).map((_, idx1: number) => (
|
||||
<div
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import Image from 'next/image'
|
||||
import { detectBrowser, isBrowser } from 'common'
|
||||
import { cn } from 'ui'
|
||||
|
||||
const DatabaseVisual = () => {
|
||||
interface Props {
|
||||
className?: string
|
||||
}
|
||||
|
||||
const DatabaseVisual: React.FC<Props> = ({ className }) => {
|
||||
const containerRef = useRef(null)
|
||||
const ref = useRef(null)
|
||||
const [gradientPos, setGradientPos] = useState({ x: 0, y: 0 })
|
||||
@@ -49,7 +54,7 @@ const DatabaseVisual = () => {
|
||||
|
||||
return (
|
||||
<figure
|
||||
className="absolute inset-0 z-0"
|
||||
className={cn('absolute inset-0 z-0', className)}
|
||||
ref={containerRef}
|
||||
role="img"
|
||||
aria-label="Supabase Postgres database visual composition"
|
||||
|
||||
@@ -3,8 +3,13 @@ import Image from 'next/image'
|
||||
import Typed from 'typed.js'
|
||||
import type { TypedOptions } from 'typed.js'
|
||||
import { useBreakpoint } from 'common'
|
||||
import { cn } from 'ui'
|
||||
|
||||
const FunctionsVisual = () => {
|
||||
interface Props {
|
||||
className?: string
|
||||
}
|
||||
|
||||
const FunctionsVisual: React.FC<Props> = ({ className }) => {
|
||||
const typerRef = useRef<HTMLSpanElement>(null)
|
||||
const isMobile = useBreakpoint('md')
|
||||
const [typed, setTyped] = useState<Typed | null>(null)
|
||||
@@ -64,7 +69,7 @@ const FunctionsVisual = () => {
|
||||
|
||||
return (
|
||||
<figure
|
||||
className="absolute inset-0 z-20"
|
||||
className={cn('absolute inset-0 z-20', className)}
|
||||
role="img"
|
||||
aria-label="Supabase Edge Functions visual composition"
|
||||
onMouseOver={handleMouseEnter}
|
||||
|
||||
@@ -30,7 +30,7 @@ const ProductCard = ({
|
||||
<Link
|
||||
href={url}
|
||||
className={cn(
|
||||
'group relative w-full h-[400px] flex flex-col gap-5 lg:flex-row focus:outline-none focus:border-none focus:ring-brand-600 focus:ring-2 focus:rounded-xl',
|
||||
'group relative w-full sm:h-[400px] flex flex-col gap-5 lg:flex-row focus:outline-none focus:border-none focus:ring-brand-600 focus:ring-2 focus:rounded-xl',
|
||||
className
|
||||
)}
|
||||
onClick={onClick}
|
||||
@@ -41,17 +41,22 @@ const ProductCard = ({
|
||||
hasMotion={title.includes('Edge Functions')}
|
||||
outerClassName="relative w-full h-full"
|
||||
innerClassName={cn(
|
||||
'relative overflow-hidden flex-1 flex flex-col items-center gap-5 lg:items-start justify-between',
|
||||
'bg-surface-75 w-full h-full text-foreground-lighter [&_strong]:!font-normal [&_strong]:!text-foreground'
|
||||
'relative overflow-hidden flex-1 flex flex-row sm:flex-col gap-4 items-start sm:items-center lg:items-start justify-between',
|
||||
'bg-surface-75 w-full h-full text-foreground-lighter [&_strong]:!font-normal [&_strong]:!text-foreground',
|
||||
'p-4 sm:py-6'
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'relative z-10 flex flex-col lg:h-full gap-4 mx-auto items-center text-center h-full px-4 py-6',
|
||||
alignLeft && !isDatabase && 'lg:mx-0 lg:pl-6 lg:items-start lg:text-left',
|
||||
'relative z-10',
|
||||
'h-full w-full',
|
||||
'mx-auto gap-2 sm:gap-4',
|
||||
'flex flex-col items-start sm:items-center',
|
||||
'text-left sm:text-center',
|
||||
alignLeft && !isDatabase && 'lg:mx-0 lg:pl-2 lg:items-start lg:text-left',
|
||||
alignLeft &&
|
||||
isDatabase &&
|
||||
'md:ml-4 md:mt-2 md:justify-start md:max-w-[260px] md:text-left md:items-start'
|
||||
'md:ml-2 md:mt-2 lg:pl-0 md:justify-start md:max-w-[250px] md:text-left md:items-start'
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-2 text-foreground">
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import React, { useRef, useState } from 'react'
|
||||
import Image from 'next/image'
|
||||
import { isBrowser, useReducedMotion } from 'common'
|
||||
import { cn } from 'ui'
|
||||
interface Props {
|
||||
className?: string
|
||||
}
|
||||
|
||||
const RealtimeVisual = () => {
|
||||
const RealtimeVisual: React.FC<Props> = ({ className }) => {
|
||||
const cardRef = useRef<HTMLDivElement | null>(null)
|
||||
const [svgTransformSelf, setSvgTransformSelf] = useState<string>('translate(0px, 0px)')
|
||||
const [svgTransform, setSvgTransform] = useState<string>('translate(0px, 0px)')
|
||||
@@ -39,7 +43,10 @@ const RealtimeVisual = () => {
|
||||
return (
|
||||
<figure
|
||||
ref={cardRef}
|
||||
className="absolute inset-0 xl:-bottom-2 2xl:bottom-0 z-0 w-full overflow-hidden pointer-events-auto"
|
||||
className={cn(
|
||||
'absolute inset-0 xl:-bottom-2 2xl:bottom-0 z-0 w-full overflow-hidden pointer-events-auto',
|
||||
className
|
||||
)}
|
||||
role="img"
|
||||
aria-label="Supabase Realtime multiplayer app demo"
|
||||
onMouseMove={reduceMotion ? undefined : handleMouseMove}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { range } from 'lodash'
|
||||
import { File, Image, Video } from 'lucide-react'
|
||||
import { cn } from 'ui'
|
||||
|
||||
const StorageVisual = () => {
|
||||
interface Props {
|
||||
className?: string
|
||||
}
|
||||
|
||||
const StorageVisual: React.FC<Props> = ({ className }) => {
|
||||
const cols = [
|
||||
<Image className="w-6 h-6 md:w-6 md:h-6" />,
|
||||
<File className="w-6 h-6 md:w-6 md:h-6" />,
|
||||
@@ -10,7 +15,7 @@ const StorageVisual = () => {
|
||||
|
||||
return (
|
||||
<figure
|
||||
className="absolute inset-0 overflow-hidden flex nowrap"
|
||||
className={cn('absolute inset-0 overflow-hidden flex nowrap', className)}
|
||||
role="img"
|
||||
aria-label="Supabase Storage supports images, documents and videos"
|
||||
>
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
import { detectBrowser, isBrowser } from 'common'
|
||||
import Image from 'next/image'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { cn } from 'ui'
|
||||
|
||||
const VectorVisual = () => {
|
||||
interface Props {
|
||||
className?: string
|
||||
}
|
||||
|
||||
const VectorVisual: React.FC<Props> = ({ className }) => {
|
||||
const containerRef = useRef(null)
|
||||
const ref = useRef(null)
|
||||
const [gradientPos, setGradientPos] = useState({ x: 0, y: 0 })
|
||||
@@ -49,15 +54,16 @@ const VectorVisual = () => {
|
||||
|
||||
return (
|
||||
<figure
|
||||
className="
|
||||
absolute inset-0
|
||||
z-0 flex items-end
|
||||
top-auto
|
||||
aspect-[390/430]
|
||||
w-full md:w-[calc(100%+4rem)] 2xl:w-full
|
||||
md:-mx-8 2xl:mx-0
|
||||
-bottom-0 sm:-bottom-28 md:bottom-0 lg:-bottom-28 xl:bottom-0
|
||||
"
|
||||
className={cn(
|
||||
'absolute inset-0',
|
||||
'z-0 flex items-end',
|
||||
'top-auto',
|
||||
'aspect-[390/430]',
|
||||
'w-full md:w-[calc(100%+4rem)] 2xl:w-full',
|
||||
'md:-mx-8 2xl:mx-0',
|
||||
'-bottom-0 sm:-bottom-28 md:bottom-0 lg:-bottom-28 xl:bottom-0',
|
||||
className
|
||||
)}
|
||||
ref={containerRef}
|
||||
role="img"
|
||||
aria-label="Supabase Vector uses pgvector to store, index, and access embeddings"
|
||||
|
||||
@@ -22,7 +22,7 @@ interface Props {
|
||||
products: SolutionsType
|
||||
}
|
||||
|
||||
const Products = (props: Props) => {
|
||||
const Products: React.FC<Props> = (props) => {
|
||||
const router = useRouter()
|
||||
const telemetryProps = useTelemetryProps()
|
||||
|
||||
@@ -97,7 +97,7 @@ const Products = (props: Props) => {
|
||||
</ul>
|
||||
}
|
||||
onClick={() => sendTelemetryEvent(PRODUCT_SHORTNAMES.DATABASE)}
|
||||
image={<DatabaseVisual />}
|
||||
image={<DatabaseVisual className="hidden sm:block" />}
|
||||
/>
|
||||
<ProductCard
|
||||
className="col-span-6 xl:col-span-3"
|
||||
@@ -108,11 +108,10 @@ const Products = (props: Props) => {
|
||||
subtitle={
|
||||
<>
|
||||
<strong>Add user sign ups and logins</strong>,
|
||||
<br className="inline-block sm:hidden lg:inline-block" />
|
||||
securing your data with Row Level Security.
|
||||
<br className="hidden lg:inline-block" /> securing your data with Row Level Security.
|
||||
</>
|
||||
}
|
||||
image={<AuthVisual />}
|
||||
image={<AuthVisual className="hidden sm:block" />}
|
||||
onClick={() => sendTelemetryEvent(PRODUCT_SHORTNAMES.AUTHENTICATION)}
|
||||
/>
|
||||
<ProductCard
|
||||
@@ -124,12 +123,12 @@ const Products = (props: Props) => {
|
||||
subtitle={
|
||||
<>
|
||||
Easily write custom code
|
||||
<br className="inline-block sm:hidden lg:inline-block" />{' '}
|
||||
<br className="hidden sm:inline-block" />{' '}
|
||||
<strong>without deploying or scaling servers.</strong>
|
||||
</>
|
||||
}
|
||||
onClick={() => sendTelemetryEvent(PRODUCT_SHORTNAMES.FUNCTIONS)}
|
||||
image={<FunctionsVisual />}
|
||||
image={<FunctionsVisual className="hidden sm:block" />}
|
||||
/>
|
||||
<ProductCard
|
||||
className="col-span-6 xl:col-span-3"
|
||||
@@ -140,11 +139,11 @@ const Products = (props: Props) => {
|
||||
subtitle={
|
||||
<>
|
||||
<strong>Store, organize, and serve</strong>
|
||||
<br className="inline-block xl:hidden 2xl:inline-block" /> large files, from videos to
|
||||
images.
|
||||
<br className="hidden sm:inline-block xl:hidden 2xl:inline-block" /> large files, from
|
||||
videos to images.
|
||||
</>
|
||||
}
|
||||
image={<StorageVisual />}
|
||||
image={<StorageVisual className="hidden sm:block" />}
|
||||
onClick={() => sendTelemetryEvent(PRODUCT_SHORTNAMES.STORAGE)}
|
||||
/>
|
||||
<ProductCard
|
||||
@@ -155,12 +154,11 @@ const Products = (props: Props) => {
|
||||
subtitle={
|
||||
<>
|
||||
<strong>Build multiplayer experiences</strong>
|
||||
<br className="inline-block md:hidden 2xl:inline-block" /> with real-time data
|
||||
synchronization.
|
||||
<br className="hidden sm:inline-block" /> with real-time data synchronization.
|
||||
</>
|
||||
}
|
||||
onClick={() => sendTelemetryEvent(PRODUCT_SHORTNAMES.REALTIME)}
|
||||
image={<RealtimeVisual />}
|
||||
image={<RealtimeVisual className="hidden sm:block" />}
|
||||
className="
|
||||
col-span-6 pointer-events-none xl:col-span-3
|
||||
hover:!cursor-[url('/images/index/products/realtime-cursor-light.svg'),_auto]
|
||||
@@ -175,7 +173,7 @@ const Products = (props: Props) => {
|
||||
title={props.products['vector'].name}
|
||||
subtitle={
|
||||
<>
|
||||
Integrate your favorite ML-models to <br className="inline-block md:hidden" />
|
||||
Integrate your favorite ML-models to <br className="hidden sm:inline-block md:hidden" />
|
||||
<strong>store, index and search vector embeddings</strong>.
|
||||
</>
|
||||
}
|
||||
@@ -243,7 +241,7 @@ const Products = (props: Props) => {
|
||||
</ul>
|
||||
}
|
||||
onClick={() => sendTelemetryEvent(PRODUCT_SHORTNAMES.VECTOR)}
|
||||
image={<VectorVisual />}
|
||||
image={<VectorVisual className="hidden sm:block" />}
|
||||
/>
|
||||
<ProductCard
|
||||
alignLeft
|
||||
@@ -253,7 +251,7 @@ const Products = (props: Props) => {
|
||||
title={props.products['data-api'].name}
|
||||
subtitle={props.products['data-api'].description}
|
||||
onClick={() => sendTelemetryEvent('data-api')}
|
||||
image={<DataAPIsVisual />}
|
||||
image={<DataAPIsVisual className="hidden sm:block" />}
|
||||
/>
|
||||
<p className="text-xl sm:text-2xl text-foreground-lighter col-span-full tracking-[-.01rem]">
|
||||
<span className="text-foreground">Use one or all.</span> Best of breed products. Integrated
|
||||
|
||||
@@ -12,10 +12,12 @@ const TwitterSocialSection = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<SectionContainer className="w-full text-center !pb-0">
|
||||
<SectionContainer className="w-full text-center flex flex-col items-center !pb-0">
|
||||
<h3 className="h2">Join the community</h3>
|
||||
<p className="p">Discover what our community has to say about their Supabase experience.</p>
|
||||
<div className="my-8 flex justify-center gap-2">
|
||||
<p className="p max-w-[300px] md:max-w-none">
|
||||
Discover what our community has to say about their Supabase experience.
|
||||
</p>
|
||||
<div className="my-4 flex justify-center gap-2">
|
||||
<Button asChild size="small" iconRight={<MessageCircle size={14} />} type="default">
|
||||
<Link
|
||||
href={'https://github.com/supabase/supabase/discussions'}
|
||||
@@ -33,7 +35,7 @@ const TwitterSocialSection = () => {
|
||||
</div>
|
||||
</SectionContainer>
|
||||
<SectionContainer className="relative w-full !px-0 lg:!px-16 xl:!px-0 !pb-0 mb-16 md:mb-12 lg:mb-12 !pt-6 max-w-[1400px]">
|
||||
<TwitterSocialProofMobile className="lg:hidden -mb-24" tweets={tweets} />
|
||||
<TwitterSocialProofMobile className="lg:hidden -mb-32" tweets={tweets} />
|
||||
<TwitterSocialProof className="hidden lg:flex" />
|
||||
</SectionContainer>
|
||||
</>
|
||||
|
||||
@@ -1,6 +1,21 @@
|
||||
import { PRODUCT_NAMES } from 'shared-data/products'
|
||||
|
||||
export default [
|
||||
export type Example = {
|
||||
type: string
|
||||
tags: string[]
|
||||
products: PRODUCT_NAMES[]
|
||||
title: string
|
||||
description: string
|
||||
author?: string
|
||||
author_url?: string
|
||||
author_img?: string
|
||||
repo_name?: string
|
||||
repo_url?: string
|
||||
vercel_deploy_url?: string
|
||||
demo_url?: string
|
||||
}
|
||||
|
||||
const Examples: Example[] = [
|
||||
{
|
||||
type: 'example',
|
||||
tags: ['Next.js', 'Stripe', 'Vercel'],
|
||||
@@ -287,3 +302,5 @@ export default [
|
||||
'https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/file-upload-storage',
|
||||
},
|
||||
]
|
||||
|
||||
export default Examples
|
||||
@@ -51,8 +51,7 @@ export default {
|
||||
title: (
|
||||
<>
|
||||
<span className="text-foreground">Stay productive and manage your app</span>
|
||||
<br className="hidden md:block" />
|
||||
without leaving the dashboard
|
||||
<br className="hidden sm:block" /> without leaving the dashboard
|
||||
</>
|
||||
),
|
||||
tabs: [
|
||||
|
||||
@@ -137,7 +137,7 @@ export const CustomerQuote = ({
|
||||
)
|
||||
|
||||
export const CustomerLogo = ({ title, logo }: { title: string; logo: string }) => (
|
||||
<div className="relative h-8 max-h-6 w-28">
|
||||
<div className="relative h-8 max-h-5 xl:max-h-6 w-20 max-w-20 md:w-28 xl:max-w-28">
|
||||
<Image
|
||||
fill
|
||||
src={logo}
|
||||
|
||||
@@ -79,16 +79,16 @@ function CustomerStoriesPage(props: any) {
|
||||
/>
|
||||
<DefaultLayout>
|
||||
<div className="relative z-0 bg-background overflow-hidden">
|
||||
<div className="container mx-auto mt-44 px-8 sm:px-4 xl:px-20">
|
||||
<div className="container mx-auto mt-28 sm:mt-44 px-4 xl:px-20">
|
||||
<div className="mx-auto relative z-10">
|
||||
<motion.div
|
||||
className="mx-auto max-w-2xl text-center"
|
||||
className="mx-auto sm:max-w-2xl text-center"
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0, transition: { duration: 0.5, easing: 'easeOut' } }}
|
||||
>
|
||||
<WebinarAnnouncement />
|
||||
<h1 className="text-foreground mb-3 text-3xl">Customer stories</h1>
|
||||
<h2 className="text-foreground-light text-xl">
|
||||
<h2 className="text-foreground-light text-base sm:text-xl">
|
||||
Discover case studies on how Supabase is being used around the world to quickly
|
||||
create outstanding products and set new industry standards.
|
||||
</h2>
|
||||
@@ -125,7 +125,7 @@ function CustomerStoriesPage(props: any) {
|
||||
</div>
|
||||
<div
|
||||
className={[
|
||||
'absolute inset-0 h-[300px] bg-background z-0 after:!bg-background',
|
||||
'absolute inset-0 h-[150px] sm:h-[300px] bg-background z-0 after:!bg-background',
|
||||
styles['bg-visual'],
|
||||
].join(' ')}
|
||||
/>
|
||||
|
||||
@@ -115,12 +115,12 @@ function CaseStudyPage(props: any) {
|
||||
<DefaultLayout>
|
||||
<div
|
||||
className="
|
||||
container mx-auto px-8 py-16 sm:px-16
|
||||
container mx-auto p-8 sm:py-16 sm:px-16
|
||||
xl:px-20
|
||||
"
|
||||
>
|
||||
<div className="grid grid-cols-12 gap-4">
|
||||
<div className="col-span-12 mb-2 xl:col-span-2">
|
||||
<div className="hidden xl:block col-span-12 mb-2 xl:col-span-2">
|
||||
{/* Back button */}
|
||||
<Link
|
||||
href="/customers"
|
||||
@@ -131,15 +131,14 @@ function CaseStudyPage(props: any) {
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="col-span-12 lg:col-span-8
|
||||
|
||||
"
|
||||
>
|
||||
<div className="col-span-12 lg:col-span-8">
|
||||
<div>
|
||||
<article className="flex flex-col gap-8">
|
||||
<div className="flex flex-col gap-8 max-w-xxl">
|
||||
<Link href="/customers" className="text-brand hover:text-brand-600 mb-2 mt-0">
|
||||
<div className="flex flex-col gap-4 sm:gap-8 max-w-xxl">
|
||||
<Link
|
||||
href="/customers"
|
||||
className="text-brand hover:text-brand-600 sm:mb-2 mt-0"
|
||||
>
|
||||
Customer Stories
|
||||
</Link>
|
||||
<h1 className="text-foreground text-4xl font-semibold xl:text-5xl">{title}</h1>
|
||||
|
||||
@@ -66,6 +66,12 @@
|
||||
<priority>0.5</priority>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/enterprise</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.5</priority>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/ga-week</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
@@ -229,13 +235,13 @@
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/partners/experts</loc>
|
||||
<loc>https://supabase.com/partners/integrations</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.5</priority>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/partners/integrations</loc>
|
||||
<loc>https://supabase.com/partners/experts</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.5</priority>
|
||||
</url>
|
||||
@@ -2024,6 +2030,12 @@
|
||||
<priority>0.5</priority>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/blog/lw12-hackathon-winners</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.5</priority>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/customers/berriai</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
|
||||
Reference in new issue
Block a user