mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 01:45:10 +03:00
feat: enterprise page (#29555)
* init enterprise page * security section * cta form * support and quote sections * heading * add company email validation in request demo form * add performance section * remove og atm * enterprise og * enterprise og * enterprise og * remove full stops on headings * fix nav * max w * update values
This commit is contained in:
1 parent
d00a939729
commit
e74ea22dff
19 files changed
+966
-15
No files matched your search
@@ -3,6 +3,34 @@ const corsHeaders = {
|
||||
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
|
||||
}
|
||||
|
||||
const personalEmailDomains = [
|
||||
'@gmail.com',
|
||||
'@yahoo.com',
|
||||
'@hotmail.',
|
||||
'@outlook.com',
|
||||
'@aol.com',
|
||||
'@icloud.com',
|
||||
'@live.com',
|
||||
'@protonmail.com',
|
||||
'@mail.com',
|
||||
'@example.com',
|
||||
]
|
||||
|
||||
const isValidEmail = (email: string): boolean => {
|
||||
const emailPattern = /^[\w-\.+]+@([\w-]+\.)+[\w-]{2,8}$/
|
||||
return emailPattern.test(email)
|
||||
}
|
||||
|
||||
const isCompanyEmail = (email: string): boolean => {
|
||||
for (const domain of personalEmailDomains) {
|
||||
if (email.includes(domain)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
export async function POST(req: Request) {
|
||||
const HUBSPOT_PORTAL_ID = process.env.HUBSPOT_PORTAL_ID
|
||||
const HUBSPOT_FORM_GUID = process.env.HUBSPOT_ENTERPRISE_FORM_GUID
|
||||
@@ -13,7 +41,23 @@ export async function POST(req: Request) {
|
||||
if (!firstName || !secondName || !companyEmail || !message) {
|
||||
return new Response(JSON.stringify({ message: 'All fields are required' }), {
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
|
||||
status: 400,
|
||||
status: 422,
|
||||
})
|
||||
}
|
||||
|
||||
// Validate email
|
||||
if (companyEmail && !isValidEmail(companyEmail)) {
|
||||
return new Response(JSON.stringify({ message: 'Invalid email address' }), {
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
|
||||
status: 422,
|
||||
})
|
||||
}
|
||||
|
||||
// Validate company email
|
||||
if (companyEmail && !isCompanyEmail(companyEmail)) {
|
||||
return new Response(JSON.stringify({ message: 'Please use a company email address' }), {
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
|
||||
status: 422,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
import React, { FC } from 'react'
|
||||
import SectionContainer from '~/components/Layouts/SectionContainer'
|
||||
|
||||
import { CustomerLogo, CustomerQuote } from '../../pages/contact/sales'
|
||||
import EnterpriseFormQuotes from '../EnterpriseFormQuotes'
|
||||
import RequestADemoForm from '../Forms/RequestADemoForm'
|
||||
|
||||
interface Props {}
|
||||
|
||||
const UseCases: FC<Props> = (props) => {
|
||||
return (
|
||||
<SectionContainer className="text grid gap-8 lg:gap-12 md:grid-cols-2">
|
||||
<div className="lg:pb-8 md:h-full w-full flex flex-col justify-between gap-2">
|
||||
<div className="flex flex-col gap-2 md:max-w-md">
|
||||
<h1 className="h1 !m-0">Request a demo</h1>
|
||||
<p className="md:text-lg text-foreground-lighter">
|
||||
We can take your requirements and show you how Supabase can help you achieve your goals.
|
||||
</p>
|
||||
</div>
|
||||
<EnterpriseFormQuotes
|
||||
className="hidden md:flex"
|
||||
tabs={[
|
||||
{
|
||||
label: <CustomerLogo title="Goodtape" logo="/images/customers/logos/good-tape.png" />,
|
||||
panel: (
|
||||
<CustomerQuote
|
||||
quote="My biggest regret is not having gone with Supabase from the beginning."
|
||||
author="Jakob Steinn, Co-founder & Tech Lead, Good Tape"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
label: <CustomerLogo title="Xendit" logo="/images/customers/logos/xendit.png" />,
|
||||
panel: (
|
||||
<CustomerQuote
|
||||
quote="The full solution was built and in production in less than one week."
|
||||
author="Developer, Xendit"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
label: <CustomerLogo title="Chatbase" logo="/images/customers/logos/chatbase.png" />,
|
||||
panel: (
|
||||
<CustomerQuote
|
||||
quote="Supabase is great because it has everything. I don’t need a different solution for authentication, a different solution for database, or a different solution for storage."
|
||||
author="Yasser Elsaid, Founder, Chatbase"
|
||||
className="max-w-none"
|
||||
/>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
<RequestADemoForm />
|
||||
<EnterpriseFormQuotes
|
||||
className="md:hidden mt-4"
|
||||
tabs={[
|
||||
{
|
||||
label: <CustomerLogo title="Goodtape" logo="/images/customers/logos/good-tape.png" />,
|
||||
panel: (
|
||||
<CustomerQuote
|
||||
quote="My biggest regret is not having gone with Supabase from the beginning."
|
||||
author="Jakob Steinn Co-founder & Tech Lead"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
label: <CustomerLogo title="Xendit" logo="/images/customers/logos/xendit.png" />,
|
||||
panel: (
|
||||
<CustomerQuote
|
||||
quote="The full solution was built and in production in less than one week."
|
||||
author="Xendit developer"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
label: <CustomerLogo title="Chatbase" logo="/images/customers/logos/chatbase.png" />,
|
||||
panel: (
|
||||
<CustomerQuote
|
||||
quote="Supabase is great because it has everything. I don’t need a different solution for authentication, a different solution for database, or a different solution for storage."
|
||||
author="Yasser Elsaid, Founder, Chatbase"
|
||||
className="max-w-none"
|
||||
/>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</SectionContainer>
|
||||
)
|
||||
}
|
||||
|
||||
export default UseCases
|
||||
@@ -0,0 +1,111 @@
|
||||
import React, { FC } from 'react'
|
||||
import { cn, Badge } from 'ui'
|
||||
|
||||
import SectionContainer from '~/components/Layouts/SectionContainer'
|
||||
|
||||
interface Props {
|
||||
id: string
|
||||
heading: string | JSX.Element
|
||||
subheading: string | JSX.Element
|
||||
highlights: Highlight[]
|
||||
}
|
||||
|
||||
type Highlight = {
|
||||
heading: string
|
||||
subheading: string
|
||||
}
|
||||
|
||||
const Performance: FC<Props> = (props) => {
|
||||
return (
|
||||
<SectionContainer id={props.id} className="relative">
|
||||
<div className="relative z-10 flex flex-col gap-4 md:gap-8 pb-20">
|
||||
<div className="flex flex-col gap-2 max-w-xl">
|
||||
{/* <span className="label">{props.label}</span> */}
|
||||
<h2 className="h2 !m-0">{props.heading}</h2>
|
||||
<p className="p !text-foreground-lighter">{props.subheading}</p>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-4 md:gap-12">
|
||||
{props.highlights.map((highlight) => (
|
||||
<HighlightItem key={highlight.heading} highlight={highlight} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative xl:absolute z-0 inset-0 mt-4 -mb-8 sm:mt-0 sm:-mb-20 md:-mt-20 md:-mb-36 xl:mt-0 xl:top-10 w-full aspect-[2.15/1]">
|
||||
<GraphLabel className="" />
|
||||
<svg
|
||||
width="100%"
|
||||
height="100%"
|
||||
viewBox="0 0 1403 599"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="absolute inset-0 w-full h-full"
|
||||
>
|
||||
<path
|
||||
d="M1402.27 0.744141C896.689 410.854 286.329 492.876 0.476562 492.876V598.744H1402.27V0.744141Z"
|
||||
fill="url(#paint0_linear_585_9420)"
|
||||
/>
|
||||
<path
|
||||
d="M11.4209 492.744C295.041 492.744 900.636 410.744 1402.27 0.744141"
|
||||
stroke="hsl(var(--foreground-lighter))"
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="paint0_linear_585_9420"
|
||||
x1="701.374"
|
||||
y1="170.846"
|
||||
x2="701.374"
|
||||
y2="561.839"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="hsl(var(--border-overlay))" />
|
||||
<stop offset="1" stopColor="hsl(var(--border-overlay))" stopOpacity="0" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<div className="absolute inset-0 w-full h-full bg-[radial-gradient(50%_50%_at_50%_50%,_transparent_0%,_hsl(var(--background-default))_100%)]" />
|
||||
</div>
|
||||
</SectionContainer>
|
||||
)
|
||||
}
|
||||
|
||||
const GraphLabel: FC<{ className?: string }> = ({ className }) => (
|
||||
<div
|
||||
className={cn(
|
||||
'absolute z-10 inset-0 left-auto right-[20%] -top-8 md:top-0 xl:top-[15%] w-fit h-[200px] lg:h-[400px]',
|
||||
'flex flex-col items-center gap-1',
|
||||
className
|
||||
)}
|
||||
>
|
||||
<div className="w-fit text-foreground bg-alternative p-4 rounded-lg border flex flex-col gap-1">
|
||||
<span className="label !text-[10px] !leading-3">Users</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-foreground-light text-2xl">930,550</span>
|
||||
<Badge variant="success" size="small" className="h-[24px] px-2">
|
||||
+13.4%
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
'relative w-2 h-2 min-w-2 min-h-2 rounded-full border-2 border-stronger',
|
||||
'after:absolute after:inset-0 after:top-full after:mx-auto after:w-[2px] after:h-[150px] after:lg:h-[250px]',
|
||||
'after:bg-gradient-to-b after:from-border-stronger after:to-transparent'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
interface HighlightItemProps {
|
||||
highlight: Highlight
|
||||
}
|
||||
|
||||
const HighlightItem: FC<HighlightItemProps> = ({ highlight }) => {
|
||||
return (
|
||||
<li className="flex flex-col gap-2 text-sm">
|
||||
<span className="label">{highlight.heading}</span>
|
||||
<p className="text-foreground text-xl md:text-3xl">{highlight.subheading}</p>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
export default Performance
|
||||
@@ -0,0 +1,68 @@
|
||||
import React, { FC } from 'react'
|
||||
import type { LucideIcon } from 'lucide-react'
|
||||
|
||||
import { TextLink } from 'ui'
|
||||
import SectionContainer from '~/components/Layouts/SectionContainer'
|
||||
|
||||
interface Props {
|
||||
id: string
|
||||
label: string | JSX.Element
|
||||
heading: string | JSX.Element
|
||||
subheading: string | JSX.Element
|
||||
features: FeatureProps[]
|
||||
cta: {
|
||||
label: string
|
||||
url: string
|
||||
}
|
||||
}
|
||||
|
||||
export type Story = {
|
||||
url: string
|
||||
heading: string
|
||||
subheading: string | JSX.Element
|
||||
}
|
||||
|
||||
type FeatureProps = {
|
||||
icon: LucideIcon
|
||||
heading: string
|
||||
}
|
||||
|
||||
const EnterpriseSecurity: FC<Props> = (props) => {
|
||||
return (
|
||||
<SectionContainer
|
||||
id={props.id}
|
||||
className="flex flex-col xl:flex-row justify-between gap-4 md:gap-8"
|
||||
>
|
||||
<div className="flex flex-col gap-2 max-w-xl">
|
||||
<span className="label">{props.label}</span>
|
||||
<h2 className="h2 !m-0">{props.heading}</h2>
|
||||
<p className="p !text-foreground-lighter">{props.subheading}</p>
|
||||
<TextLink hasChevron label={props.cta.label} url={props.cta.url} className="mt-2" />
|
||||
</div>
|
||||
<ul className="grid grid-cols-2 sm:grid-cols-2 gap-4 md:gap-x-20 h-fit xl:grid-cols-2 mt-4 xl:mt-8">
|
||||
{props.features.map((feature) => (
|
||||
<FeatureItem feature={feature} key={feature.heading} />
|
||||
))}
|
||||
</ul>
|
||||
</SectionContainer>
|
||||
)
|
||||
}
|
||||
|
||||
interface FeatureItemProps {
|
||||
feature: FeatureProps
|
||||
}
|
||||
|
||||
const FeatureItem: FC<FeatureItemProps> = ({ feature }) => {
|
||||
const Icon: LucideIcon = feature.icon
|
||||
|
||||
return (
|
||||
<li className="flex flex-nowrap items-center h-fit gap-2 sm:gap-4 text-foreground text-xs sm:text-sm">
|
||||
<figure className="border not-prose bg-surface-100 flex h-8 w-8 items-center justify-center rounded-md">
|
||||
<Icon className="w-4 h-4 stroke-1" />
|
||||
</figure>
|
||||
<p>{feature.heading}</p>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
export default EnterpriseSecurity
|
||||
@@ -0,0 +1,33 @@
|
||||
import React, { FC } from 'react'
|
||||
|
||||
import SectionContainer from '~/components/Layouts/SectionContainer'
|
||||
|
||||
interface Props {
|
||||
id: string
|
||||
quote: Quote
|
||||
}
|
||||
|
||||
type Quote = {
|
||||
text: string
|
||||
author: string
|
||||
logo: string | JSX.Element
|
||||
role: string
|
||||
}
|
||||
|
||||
const SingleQuote: FC<Props> = ({ id: sectionId, quote }) => {
|
||||
return (
|
||||
<SectionContainer
|
||||
id={sectionId}
|
||||
className="flex flex-col items-center text-center gap-8 md:gap-12"
|
||||
>
|
||||
<q className="text-2xl max-w-xs md:text-3xl md:max-w-xl">{quote.text}</q>
|
||||
<div className="flex flex-col items-center gap-1">
|
||||
<figure className="text-foreground-lighter mb-4">{quote.logo}</figure>
|
||||
<span className="text-foreground">{quote.author}</span>
|
||||
<span className="text-foreground-lighter">{quote.role}</span>
|
||||
</div>
|
||||
</SectionContainer>
|
||||
)
|
||||
}
|
||||
|
||||
export default SingleQuote
|
||||
@@ -0,0 +1,60 @@
|
||||
import React, { FC } from 'react'
|
||||
|
||||
import { cn } from 'ui'
|
||||
import SectionContainer from '~/components/Layouts/SectionContainer'
|
||||
|
||||
import type { LucideIcon } from 'lucide-react'
|
||||
|
||||
interface Props {
|
||||
id: string
|
||||
label: string | JSX.Element
|
||||
heading: string | JSX.Element
|
||||
features: Feature[]
|
||||
}
|
||||
|
||||
type Feature = {
|
||||
icon: LucideIcon
|
||||
heading: string
|
||||
subheading: string
|
||||
}
|
||||
|
||||
const Support: FC<Props> = (props) => {
|
||||
return (
|
||||
<SectionContainer id={props.id} className="flex flex-col gap-4 md:gap-8">
|
||||
<div className="flex flex-col gap-2">
|
||||
<span className="label">{props.label}</span>
|
||||
<h2 className="h2">{props.heading}</h2>
|
||||
</div>
|
||||
<ul className="grid grid-cols-1 gap-4 gap-y-10 md:grid-cols-3 md:gap-12 xl:gap-20">
|
||||
{props.features.map((feature) => (
|
||||
<FeatureItem feature={feature} key={feature.heading} />
|
||||
))}
|
||||
</ul>
|
||||
</SectionContainer>
|
||||
)
|
||||
}
|
||||
|
||||
interface FeatureItemProps {
|
||||
feature: Feature
|
||||
}
|
||||
|
||||
const FeatureItem: FC<FeatureItemProps> = ({ feature }) => {
|
||||
const Icon: LucideIcon = feature.icon
|
||||
const iconSize = 7
|
||||
const iconWidth = `w-${iconSize}`
|
||||
const iconHeight = `h-${iconSize}`
|
||||
|
||||
return (
|
||||
<li className="flex flex-col gap-2 text-sm">
|
||||
<Icon className={cn('stroke-1 mb-2 text-foreground-lighter', iconWidth, iconHeight)} />
|
||||
<div className="w-full h-px overflow-hidden flex items-start bg-border-muted">
|
||||
<span className={cn('h-full bg-foreground-lighter', iconWidth)} />
|
||||
</div>
|
||||
<h4 className="text-foreground text-lg lg:text-xl mt-1">{feature.heading}</h4>
|
||||
<p className="text-foreground-lighter text-sm">{feature.subheading}</p>
|
||||
{/* <TextLink hasChevron label="Read story" url={feature.url} className="mt-4" /> */}
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
export default Support
|
||||
@@ -0,0 +1,141 @@
|
||||
import 'swiper/css'
|
||||
|
||||
import React, { FC } from 'react'
|
||||
import { Swiper, SwiperSlide } from 'swiper/react'
|
||||
import Link from 'next/link'
|
||||
|
||||
import { TextLink } from 'ui'
|
||||
import SectionContainer from '~/components/Layouts/SectionContainer'
|
||||
import Panel from '~/components/Panel'
|
||||
|
||||
import type { LucideIcon } from 'lucide-react'
|
||||
import Image from 'next/image'
|
||||
|
||||
interface Props {
|
||||
id: string
|
||||
label: string | JSX.Element
|
||||
heading: string | JSX.Element
|
||||
stories: Story[]
|
||||
highlights: Highlight[]
|
||||
}
|
||||
|
||||
export type Story = {
|
||||
icon: string
|
||||
url: string
|
||||
target?: '_blank' | string
|
||||
heading: string
|
||||
subheading: string | JSX.Element
|
||||
}
|
||||
|
||||
type Highlight = {
|
||||
icon: LucideIcon
|
||||
heading: string
|
||||
subheading: string
|
||||
url: string
|
||||
}
|
||||
|
||||
const UseCases: FC<Props> = (props) => (
|
||||
<section id={props.id}>
|
||||
<SectionContainer className="flex flex-col gap-4 md:gap-8 !pb-0">
|
||||
<div className="flex flex-col gap-2">
|
||||
<span className="label">{props.label}</span>
|
||||
<h2 className="h2">{props.heading}</h2>
|
||||
</div>
|
||||
</SectionContainer>
|
||||
<div className="overflow-hidden">
|
||||
<SectionContainer className="!py-4">
|
||||
<ul className="hidden xl:flex flex-col gap-4 md:flex-row items-stretch w-full h-auto min-h-[300px]">
|
||||
{props.stories.map((story) => (
|
||||
<li key={story.heading} className="w-full">
|
||||
<StoryCard story={story} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<div className="xl:hidden">
|
||||
<Swiper
|
||||
style={{ zIndex: 0, marginRight: '1px' }}
|
||||
initialSlide={0}
|
||||
spaceBetween={12}
|
||||
slidesPerView={1.2}
|
||||
speed={300}
|
||||
watchOverflow
|
||||
threshold={2}
|
||||
updateOnWindowResize
|
||||
className="h-[300px] w-full !overflow-visible"
|
||||
breakpoints={{
|
||||
320: {
|
||||
slidesPerView: 1.2,
|
||||
},
|
||||
540: {
|
||||
slidesPerView: 1.5,
|
||||
},
|
||||
900: {
|
||||
slidesPerView: 2.5,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{props.stories.map((story: Story, i: number) => (
|
||||
<SwiperSlide key={`${story.heading}-mobile`}>
|
||||
<StoryCard story={story} />
|
||||
</SwiperSlide>
|
||||
))}
|
||||
</Swiper>
|
||||
</div>
|
||||
</SectionContainer>
|
||||
</div>
|
||||
<SectionContainer className="!pt-0">
|
||||
<ul className="grid grid-cols-2 gap-4 sm:gap-10 gap-y-10 lg:grid-cols-4 md:gap-12 lg:gap-x-8 mt-8">
|
||||
{props.highlights.map((highlight) => (
|
||||
<HighlightCard highlight={highlight} key={highlight.heading} />
|
||||
))}
|
||||
</ul>
|
||||
</SectionContainer>
|
||||
</section>
|
||||
)
|
||||
|
||||
interface StoryCardProps {
|
||||
story: Story
|
||||
}
|
||||
|
||||
const StoryCard: FC<StoryCardProps> = ({ story }) => (
|
||||
<Link href={story.url} target={story.target ?? undefined} className="w-full h-full">
|
||||
<Panel
|
||||
outerClassName="w-full h-full"
|
||||
hasActiveOnHover
|
||||
innerClassName="flex flex-col justify-between text-foreground-lighter bg-surface-75 p-2"
|
||||
>
|
||||
<div className="flex flex-col justify-between gap-6 p-3 md:max-w-[230px]">
|
||||
<Image
|
||||
src={story.icon}
|
||||
alt={story.heading}
|
||||
width={150}
|
||||
height={30}
|
||||
className="max-h-[23px] max-w-[150px] w-auto object-contain object-left-bottom filter invert dark:invert-0 opacity-60"
|
||||
/>
|
||||
<h3 className="text-foreground">{story.heading}</h3>
|
||||
</div>
|
||||
<div className="p-3 bg-surface-200 rounded-lg">
|
||||
<q className="text-sm block">{story.subheading}</q>
|
||||
</div>
|
||||
</Panel>
|
||||
</Link>
|
||||
)
|
||||
|
||||
interface HighlightCardProps {
|
||||
highlight: Highlight
|
||||
}
|
||||
|
||||
const HighlightCard: FC<HighlightCardProps> = ({ highlight }) => {
|
||||
const Icon: LucideIcon = highlight.icon
|
||||
|
||||
return (
|
||||
<li className="text-foreground text-sm max-w-[250px]">
|
||||
<Icon className="stroke-1 mb-2" />
|
||||
<h4 className="text-foreground text-xl lg:text-2xl">{highlight.heading}</h4>
|
||||
<p className="text-foreground-lighter text-sm">{highlight.subheading}</p>
|
||||
<TextLink hasChevron label="Read story" url={highlight.url} className="mt-4" />
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
export default UseCases
|
||||
@@ -68,6 +68,29 @@ const isValidEmail = (email: string): boolean => {
|
||||
return emailPattern.test(email)
|
||||
}
|
||||
|
||||
const personalEmailDomains = [
|
||||
'@gmail.com',
|
||||
'@yahoo.com',
|
||||
'@hotmail.',
|
||||
'@outlook.com',
|
||||
'@aol.com',
|
||||
'@icloud.com',
|
||||
'@live.com',
|
||||
'@protonmail.com',
|
||||
'@mail.com',
|
||||
'@example.com',
|
||||
]
|
||||
|
||||
const isCompanyEmail = (email: string): boolean => {
|
||||
for (const domain of personalEmailDomains) {
|
||||
if (email.includes(domain)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
const defaultFormValue: FormData = {
|
||||
firstName: '',
|
||||
secondName: '',
|
||||
@@ -111,6 +134,11 @@ const RequestADemoForm: FC<Props> = ({ className }) => {
|
||||
newErrors.companyEmail = 'Invalid email address'
|
||||
}
|
||||
|
||||
// Validate company email
|
||||
if (formData.companyEmail && !isCompanyEmail(formData.companyEmail)) {
|
||||
newErrors.companyEmail = 'Please use a company email address'
|
||||
}
|
||||
|
||||
setErrors(newErrors)
|
||||
|
||||
// Return validation status, also check if honeypot is filled (indicating a bot)
|
||||
@@ -170,7 +198,7 @@ const RequestADemoForm: FC<Props> = ({ className }) => {
|
||||
className
|
||||
)}
|
||||
>
|
||||
<div className="border rounded-xl bg-surface-75 p-4 md:p-6 w-full md:max-w-lg min-h-[200px] md:min-h-[400px]">
|
||||
<div className="border rounded-xl bg-surface-75 p-4 md:p-6 w-full lg:max-w-lg min-h-[200px] md:min-h-[400px]">
|
||||
{success ? (
|
||||
<div className="flex flex-col h-full w-full min-w-[300px] gap-4 items-center justify-center opacity-0 transition-opacity animate-fade-in scale-1">
|
||||
<p className="text-center text-sm">{success}</p>
|
||||
|
||||
@@ -16,7 +16,7 @@ const Hero = () => {
|
||||
|
||||
return (
|
||||
<div className="relative -mt-[65px]">
|
||||
<SectionContainer className="pt-8 md:pt-16 overflow-hidden">
|
||||
<SectionContainer className="pt-8 pb-10 md:pt-16 overflow-hidden">
|
||||
<div className="relative">
|
||||
<div className="mx-auto">
|
||||
<div className="mx-auto max-w-2xl lg:col-span-6 lg:flex lg:items-center justify-center text-center">
|
||||
|
||||
@@ -1,22 +1,30 @@
|
||||
import React from 'react'
|
||||
import { Button } from 'ui'
|
||||
import { Button, cn } from 'ui'
|
||||
import Link from 'next/link'
|
||||
import ProductIcon from '../ProductIcon'
|
||||
import SectionContainer from '../Layouts/SectionContainer'
|
||||
import { CTA } from '~/types/common'
|
||||
|
||||
interface Props {
|
||||
label?: string | React.ReactNode
|
||||
h1: string | React.ReactNode
|
||||
subheader?: string[] | React.ReactNode[]
|
||||
icon?: string
|
||||
title?: string
|
||||
image?: React.ReactNode
|
||||
footer?: React.ReactNode
|
||||
footerPosition?: 'bottom' | 'left'
|
||||
ctas?: CTA[]
|
||||
className?: string
|
||||
}
|
||||
|
||||
const ProductHeader = (props: Props) => (
|
||||
<div className="w-full max-w-full relative mx-auto py-16 lg:py-24 border-b bg-alternative overflow-hidden">
|
||||
<div
|
||||
className={cn(
|
||||
'w-full max-w-full relative mx-auto py-16 lg:py-24 border-b bg-alternative overflow-hidden',
|
||||
props.className
|
||||
)}
|
||||
>
|
||||
<SectionContainer className="!py-0 grid grid-cols-12">
|
||||
<div className="relative z-10 col-span-12 gap-8 lg:col-span-5">
|
||||
<div>
|
||||
@@ -54,13 +62,18 @@ const ProductHeader = (props: Props) => (
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
{props.footer && props.footerPosition === 'left' && (
|
||||
<div className="relative z-10 mt-4 md:mt-8 lg:mt-20 xl:mt-32 col-span-12">
|
||||
{props.footer}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{props.image && (
|
||||
<div className="relative min-h-[300px] col-span-12 mt-8 lg:col-span-7 lg:mt-0 xl:col-span-6 xl:col-start-7">
|
||||
{props.image}
|
||||
</div>
|
||||
)}
|
||||
{props.footer && (
|
||||
{props.footer && props.footerPosition !== 'left' && (
|
||||
<div className="relative z-10 mt-4 md:mt-8 lg:mt-20 xl:mt-32 col-span-12">
|
||||
{props.footer}
|
||||
</div>
|
||||
|
||||
@@ -1,18 +1,27 @@
|
||||
import { cn } from 'ui'
|
||||
import { range } from 'lodash'
|
||||
|
||||
const Logos: React.FC = () => {
|
||||
interface Props {
|
||||
showHeading?: boolean
|
||||
className?: string
|
||||
align?: 'left' | 'center'
|
||||
maskBackgroundColor?: 'hsl(var(--background-default)' | 'hsl(var(--background-alternative)'
|
||||
}
|
||||
|
||||
const Logos: React.FC<Props> = ({ className, showHeading = true, align = 'center' }) => {
|
||||
const gap = 'gap-4 lg:gap-8'
|
||||
|
||||
return (
|
||||
<div className="pb-14 md:pb-24" suppressHydrationWarning>
|
||||
<div className="max-w-xl md:max-w-3xl lg:max-w-5xl mx-auto animadura px-5 lg:px-12">
|
||||
<div className={cn('pb-14 md:pb-24', className)} suppressHydrationWarning>
|
||||
<div className="max-w-md md:max-w-lg lg:max-w-2xl mx-auto">
|
||||
<div
|
||||
className={cn(
|
||||
'relative w-full mx-auto max-w-4xl opacity-90 dark:opacity-70',
|
||||
'overflow-hidden',
|
||||
'flex flex-nowrap justify-center',
|
||||
"before:content[''] before:absolute before:inset-0 before:w-full before:bg-[linear-gradient(to_right,hsl(var(--background-default))_0%,transparent_10%,transparent_90%,hsl(var(--background-default))_100%)] before:z-10",
|
||||
'flex flex-nowrap justify-center',
|
||||
'px-5 lg:px-12',
|
||||
align === 'left' ? 'justify-start ml-0' : 'justify-center',
|
||||
gap
|
||||
)}
|
||||
>
|
||||
@@ -29,9 +38,11 @@ const Logos: React.FC = () => {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<p className="w-full text-center text-sm text-foreground-lighter mt-6 lg:mt-8">
|
||||
Trusted by fast-growing companies worldwide
|
||||
</p>
|
||||
{showHeading && (
|
||||
<p className="w-full text-center text-sm text-foreground-lighter mt-6 lg:mt-8">
|
||||
Trusted by fast-growing companies worldwide
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,291 @@
|
||||
import {
|
||||
Activity,
|
||||
ArrowLeftRight,
|
||||
ClipboardCheck,
|
||||
DollarSign,
|
||||
FolderLock,
|
||||
Globe2,
|
||||
LayoutList,
|
||||
Lightbulb,
|
||||
LineChart,
|
||||
Lock,
|
||||
Scale,
|
||||
ShieldAlert,
|
||||
ShieldCheck,
|
||||
Timer,
|
||||
Users,
|
||||
UserX,
|
||||
} from 'lucide-react'
|
||||
import RequestADemoForm from '../../components/Forms/RequestADemoForm'
|
||||
|
||||
export default {
|
||||
metadata: {
|
||||
metaTitle: 'Supabase for Enterprise',
|
||||
metaDescription:
|
||||
'Leading enterprises use Supabase to build faster, better, and more scalable products.',
|
||||
},
|
||||
heroSection: {
|
||||
id: 'hero',
|
||||
title: 'Supabase for Enterprise',
|
||||
h1: (
|
||||
<>
|
||||
Innovative Enterprises
|
||||
<span className="block">use Supabase</span>
|
||||
</>
|
||||
),
|
||||
subheader: [
|
||||
<>
|
||||
Leading enterprises use Supabase to build faster, better, and more scalable products. From
|
||||
GitHub to PwC, innovative companies trust Supabase to drive their digital transformation
|
||||
strategy.
|
||||
</>,
|
||||
],
|
||||
image: <RequestADemoForm />,
|
||||
logos: [
|
||||
{
|
||||
name: 'GitHub',
|
||||
image: '/images/enterprise/github.svg',
|
||||
},
|
||||
{
|
||||
name: 'PwC',
|
||||
image: '/images/enterprise/pwc.svg',
|
||||
},
|
||||
],
|
||||
},
|
||||
'use-cases': {
|
||||
id: 'use-cases',
|
||||
label: <>Build with Supabase</>,
|
||||
heading: <>Stay on the forefront</>,
|
||||
stories: [
|
||||
{
|
||||
icon: '/images/customers/logos/light/mozilla.png',
|
||||
url: 'https://developer.mozilla.org/en-US/blog/introducing-ai-help/',
|
||||
target: '_blank',
|
||||
heading: 'Mozilla use Supabase for GenAI and RAG',
|
||||
subheading: (
|
||||
<>
|
||||
We store embeddings in a PostgreSQL database, hosted by Supabase, to perform a{' '}
|
||||
<span className="text-foreground">
|
||||
similarity search to identify the most relevant sections within the MDN
|
||||
</span>
|
||||
.
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
icon: '/images/customers/logos/light/epsilon3.png',
|
||||
url: '/customers/epsilon3',
|
||||
heading: 'Epsilon3 use Supabase to build software for NASA',
|
||||
subheading: (
|
||||
<>
|
||||
<span className="text-foreground">
|
||||
Billion dollar missions need to run reliably and securely
|
||||
</span>
|
||||
. We use Supabase because they give us an open-source scalable back-end built by
|
||||
database experts that we can self-host.
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
icon: '/images/customers/logos/light/pebblely.png',
|
||||
url: '/customers/pebblely',
|
||||
heading: 'Pebblely use Supabase to scale to millions of users.',
|
||||
subheading: (
|
||||
<>
|
||||
It streamlined the database, the API, and authentication.{' '}
|
||||
<span className="text-foreground">Everything was up and running in two days</span>. It
|
||||
is easy to get started and provides all the solutions we require as we continue to grow.
|
||||
</>
|
||||
),
|
||||
},
|
||||
],
|
||||
highlights: [
|
||||
{
|
||||
icon: LineChart,
|
||||
heading: '100x scale',
|
||||
subheading: 'Maergo handled 100x their highest sustained traffic with Supabase.',
|
||||
url: '/customers/maergo',
|
||||
},
|
||||
{
|
||||
icon: DollarSign,
|
||||
heading: '83% cost reduction',
|
||||
subheading: 'Shotgun reduced costs by 83% by migrating to Supabase.',
|
||||
url: '/customers/shotgun',
|
||||
},
|
||||
{
|
||||
icon: Timer,
|
||||
heading: '20% faster dev',
|
||||
subheading: 'Voypost enjoyed a 20% faster development process.',
|
||||
url: '/customers/voypost',
|
||||
},
|
||||
{
|
||||
icon: Scale,
|
||||
heading: 'GDPR compliance',
|
||||
subheading: 'Markprompt use Supabase to build GDPR-compliant AI chatbots.',
|
||||
url: '/customers/markprompt',
|
||||
},
|
||||
],
|
||||
},
|
||||
performance: {
|
||||
id: 'performance',
|
||||
heading: (
|
||||
<>
|
||||
Top performance,
|
||||
<br /> at any scale
|
||||
</>
|
||||
),
|
||||
subheading:
|
||||
"Supabase ensures optimal database performance at any scale, so you can focus on innovating and growing without worrying about infrastructure limitations—whether you're handling high-traffic applications, complex queries, or massive data volumes.",
|
||||
highlights: [
|
||||
{
|
||||
heading: 'Databases managed',
|
||||
subheading: '1,000,000+',
|
||||
},
|
||||
{
|
||||
heading: 'Databases launched daily',
|
||||
subheading: '3,500+',
|
||||
},
|
||||
],
|
||||
},
|
||||
security: {
|
||||
id: 'security',
|
||||
label: 'Security',
|
||||
heading: <>Trusted for medical records, missions to the moon, and everything in between</>,
|
||||
subheading:
|
||||
'Keep your data secure with SOC 2, HIPAA, and GDPR compliance. Your customers’ data is encrypted at rest and in transit, with built-in tools for monitoring and managing security threats.',
|
||||
cta: {
|
||||
label: 'Learn more about Security',
|
||||
url: '/security',
|
||||
},
|
||||
features: [
|
||||
{
|
||||
icon: ShieldCheck,
|
||||
heading: 'SOC 2 Type II certified',
|
||||
},
|
||||
{
|
||||
icon: Activity,
|
||||
heading: 'HIPAA compliant',
|
||||
},
|
||||
{
|
||||
icon: ShieldAlert,
|
||||
heading: 'DDoS Protection',
|
||||
},
|
||||
{
|
||||
icon: Lock,
|
||||
heading: 'Multi-factor Authentication',
|
||||
},
|
||||
{
|
||||
icon: ClipboardCheck,
|
||||
heading: 'Vulnerability Management',
|
||||
},
|
||||
{
|
||||
icon: Users,
|
||||
heading: 'Role-based access control',
|
||||
},
|
||||
{
|
||||
icon: LayoutList,
|
||||
heading: 'Database Audit Logs',
|
||||
},
|
||||
{
|
||||
icon: Lightbulb,
|
||||
heading: 'Security Advisors',
|
||||
},
|
||||
{
|
||||
icon: FolderLock,
|
||||
heading: 'Encrypted Storage',
|
||||
},
|
||||
{
|
||||
icon: UserX,
|
||||
heading: 'Network restrictions',
|
||||
},
|
||||
],
|
||||
},
|
||||
support: {
|
||||
id: 'support',
|
||||
label: 'Support',
|
||||
heading: (
|
||||
<>
|
||||
Get expert help,
|
||||
<br /> whenever you need it
|
||||
</>
|
||||
),
|
||||
features: [
|
||||
{
|
||||
icon: Globe2,
|
||||
heading: 'Global Support, 24/7',
|
||||
subheading:
|
||||
'Our team has 100% global coverage. No matter where you are, we’re always available to resolve issues and keep your operations running smoothly.',
|
||||
},
|
||||
{
|
||||
icon: Users,
|
||||
heading: 'Dedicated team of experts',
|
||||
subheading:
|
||||
'Get direct access to talented engineers. From onboarding to optimizations, our expert team is here to provide personalized, hands-on support whenever you need it.',
|
||||
},
|
||||
{
|
||||
icon: ArrowLeftRight,
|
||||
heading: 'Migration & Success Support',
|
||||
subheading:
|
||||
'Our team ensures a smooth transition to Supabase while guiding you with best practices for scaling. We’re dedicated to your long-term success, every step of the way.',
|
||||
},
|
||||
],
|
||||
},
|
||||
quote: {
|
||||
id: 'quote',
|
||||
quote: {
|
||||
text: 'Supabase powers prototyping for fast-moving teams such as GitHub Next.',
|
||||
author: 'Idan Gazit',
|
||||
logo: (
|
||||
<svg
|
||||
width="91"
|
||||
height="24"
|
||||
viewBox="0 0 91 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M10.4878 10.2692C10.2941 10.2692 10.1376 10.4244 10.1376 10.6166V14.263C10.1376 14.4552 10.2941 14.6129 10.4878 14.6129H13.4204V19.1438C13.4204 19.1438 12.7624 19.3656 10.9422 19.3656C8.79419 19.3656 5.79445 18.587 5.79445 12.0431C5.79445 5.49923 8.91835 4.6369 11.851 4.6369C14.3889 4.6369 15.484 5.08038 16.1793 5.29474C16.3978 5.36126 16.599 5.14444 16.599 4.95227L17.4383 1.42901C17.4383 1.33785 17.4085 1.22944 17.3042 1.15799C17.0211 0.958425 15.2978 2.14502e-06 10.9422 2.14502e-06C5.92606 -0.00246167 0.77832 2.11642 0.77832 12.2969C0.77832 22.4774 6.67103 23.9951 11.6375 23.9951C15.7497 23.9951 18.2429 22.2507 18.2429 22.2507C18.3447 22.194 18.3571 22.0511 18.3571 21.9871V10.6166C18.3571 10.4244 18.2007 10.2692 18.007 10.2692H10.4878Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M56.7404 1.21713C56.7404 1.02248 56.5864 0.867264 56.3927 0.867264H52.1613C51.9676 0.867264 51.8112 1.02495 51.8112 1.21713V9.33046H45.2157V1.21713C45.2157 1.02248 45.0593 0.867264 44.8656 0.867264H40.6342C40.4405 0.867264 40.284 1.02495 40.284 1.21713V23.1894C40.284 23.384 40.4405 23.5417 40.6342 23.5417H44.8656C45.0593 23.5417 45.2157 23.384 45.2157 23.1894V13.7924H51.8112L51.7988 23.1894C51.7988 23.384 51.9552 23.5417 52.1489 23.5417H56.3903C56.584 23.5417 56.7379 23.384 56.7404 23.1894V1.21713Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M25.9906 4.09979C25.9906 2.58701 24.7688 1.36495 23.2615 1.36495C21.7542 1.36495 20.5324 2.58701 20.5324 4.09979C20.5324 5.61257 21.7542 6.83462 23.2615 6.83462C24.7688 6.83462 25.9906 5.6101 25.9906 4.09979Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M25.6876 8.41392C25.6876 8.22174 25.5312 8.06406 25.3375 8.06406H21.1185C20.9248 8.06406 20.7509 8.26117 20.7509 8.45581V22.9874C20.7509 23.4136 21.0191 23.5417 21.3668 23.5417H25.1686C25.5858 23.5417 25.6876 23.3397 25.6876 22.98V8.41392Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M72.8218 8.09609H68.6227C68.429 8.09609 68.2725 8.25377 68.2725 8.44842V19.2227C68.2725 19.2227 67.2047 19.9963 65.6924 19.9963C64.1802 19.9963 63.7754 19.3138 63.7754 17.8429V8.44842C63.7754 8.25377 63.6189 8.09609 63.4253 8.09609H59.164C58.9728 8.09609 58.8139 8.25377 58.8139 8.44842V18.555C58.8139 22.9233 61.2673 23.9926 64.6445 23.9926C67.4133 23.9926 69.6482 22.4749 69.6482 22.4749C69.6482 22.4749 69.755 23.2756 69.8022 23.3693C69.8494 23.4629 69.976 23.559 70.1101 23.559L72.8218 23.5467C73.013 23.5467 73.1719 23.389 73.1719 23.1968V8.44842C73.1719 8.25377 73.013 8.09609 72.8193 8.09609H72.8218Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M84.3018 7.60086C81.9154 7.60086 80.2938 8.65784 80.2938 8.65784V1.21713C80.2938 1.02248 80.1374 0.867264 79.9437 0.867264H75.6999C75.5062 0.867264 75.3497 1.02495 75.3497 1.21713V23.1894C75.3497 23.384 75.5062 23.5417 75.6999 23.5417H78.645C78.7766 23.5417 78.8784 23.4727 78.9529 23.3545C79.0249 23.2362 79.1317 22.3419 79.1317 22.3419C79.1317 22.3419 80.8675 23.9729 84.1528 23.9729C88.0092 23.9729 90.2218 22.0314 90.2218 15.2584C90.2218 8.48537 86.6882 7.60086 84.3018 7.60086ZM82.6455 19.9815C81.1878 19.9372 80.202 19.2818 80.202 19.2818V12.324C80.202 12.324 81.1754 11.7302 82.3723 11.6243C83.8846 11.4888 85.3423 11.9421 85.3423 15.522C85.3423 19.2966 84.6842 20.0407 82.6479 19.9815H82.6455Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M37.8505 8.05913H34.6769C34.6769 8.05913 34.6719 3.90022 34.6719 3.89775C34.6719 3.74007 34.59 3.66123 34.4062 3.66123H30.0804C29.9116 3.66123 29.8222 3.73514 29.8222 3.89529V8.19464C29.8222 8.19464 27.6543 8.71451 27.5078 8.75639C27.3613 8.79828 27.2545 8.93132 27.2545 9.09147V11.7943C27.2545 11.9889 27.411 12.1441 27.6047 12.1441H29.8222V18.6437C29.8222 23.4727 33.2366 23.9458 35.5386 23.9458C36.5915 23.9458 37.8505 23.6107 38.0591 23.5343C38.1857 23.4875 38.2577 23.3594 38.2577 23.219V20.2476C38.2602 20.053 38.0963 19.8978 37.9101 19.8978C37.7238 19.8978 37.252 19.9717 36.7653 19.9717C35.2058 19.9717 34.6769 19.2522 34.6769 18.3209C34.6769 17.3896 34.6769 12.1441 34.6769 12.1441H37.8505C38.0442 12.1441 38.2006 11.9865 38.2006 11.7943V8.409C38.2006 8.21435 38.0442 8.05913 37.8505 8.05913Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
role: 'Senior Director of Research, GitHub Next',
|
||||
},
|
||||
},
|
||||
'request-a-demo': {
|
||||
id: 'request-a-demo',
|
||||
heading: 'Request a demo',
|
||||
subheading:
|
||||
'We can take your requirements and show you how Supabase can help you achieve your goals.',
|
||||
quote: {
|
||||
text: 'Supabase powers prototyping for fast-moving teams such as GitHub Next.',
|
||||
author: '',
|
||||
logo: '',
|
||||
company: 'GitHub',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -21,6 +21,10 @@ export const getMenu = (latestBlogPosts: BlogPost[]) => ({
|
||||
dropdownContainerClassName: 'rounded-xl',
|
||||
subMenu: DevelopersData,
|
||||
},
|
||||
{
|
||||
title: 'Enterprise',
|
||||
url: '/enterprise',
|
||||
},
|
||||
{
|
||||
title: 'Pricing',
|
||||
url: '/pricing',
|
||||
|
||||
@@ -119,7 +119,7 @@ const ContactSales = () => {
|
||||
)
|
||||
}
|
||||
|
||||
const CustomerQuote = ({
|
||||
export const CustomerQuote = ({
|
||||
quote,
|
||||
author,
|
||||
className,
|
||||
@@ -136,7 +136,7 @@ const CustomerQuote = ({
|
||||
</div>
|
||||
)
|
||||
|
||||
const CustomerLogo = ({ title, logo }: { title: string; logo: string }) => (
|
||||
export const CustomerLogo = ({ title, logo }: { title: string; logo: string }) => (
|
||||
<div className="relative h-8 max-h-6 w-28">
|
||||
<Image
|
||||
fill
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import { NextPage } from 'next'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { NextSeo } from 'next-seo'
|
||||
|
||||
import Layout from '~/components/Layouts/Default'
|
||||
import ProductHeader from '~/components/Sections/ProductHeader2'
|
||||
|
||||
import content from '~/data/enterprise/content'
|
||||
import Logos from '../components/logos'
|
||||
|
||||
const EnterpriseUseCases = dynamic(() => import('components/Enterprise/UseCases'))
|
||||
const EnterprisePerformance = dynamic(() => import('components/Enterprise/Performance'))
|
||||
const EnterpriseSecurity = dynamic(() => import('components/Enterprise/Security'))
|
||||
const EnterpriseSupport = dynamic(() => import('components/Enterprise/Support'))
|
||||
const EnterpriseQuote = dynamic(() => import('components/Enterprise/SingleQuote'))
|
||||
const CTAForm = dynamic(() => import('components/Enterprise/CTAForm'))
|
||||
|
||||
const Enterprise: NextPage = () => (
|
||||
<>
|
||||
<NextSeo
|
||||
title={content.metadata.metaTitle}
|
||||
description={content.metadata.metaDescription}
|
||||
openGraph={{
|
||||
title: content.metadata.metaTitle,
|
||||
description: content.metadata.metaDescription,
|
||||
url: `https://supabase.com/enterprise`,
|
||||
images: [
|
||||
{
|
||||
url: `/images/enterprise/enterprise-og.png`,
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
<Layout className="overflow-visible">
|
||||
<ProductHeader
|
||||
{...content.heroSection}
|
||||
className="[&_h1]:2xl:!text-5xl bg-default border-0 lg:pb-8"
|
||||
footer={<Logos className="!pb-0" showHeading={false} align="left" />}
|
||||
footerPosition="left"
|
||||
/>
|
||||
<EnterpriseUseCases {...content['use-cases']} />
|
||||
<EnterprisePerformance {...content.performance} />
|
||||
<EnterpriseSecurity {...content.security} />
|
||||
<EnterpriseSupport {...content.support} />
|
||||
<EnterpriseQuote {...content.quote} />
|
||||
<CTAForm />
|
||||
</Layout>
|
||||
</>
|
||||
)
|
||||
|
||||
export default Enterprise
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 6.5 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 6.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 65 KiB |
@@ -101,6 +101,10 @@ h6:not(.overwrite) {
|
||||
line-height: 1.2 !important;
|
||||
}
|
||||
|
||||
.label:not(.prose *):not(.overwrite) {
|
||||
@apply text-xs font-mono uppercase text-foreground-lighter tracking-wider;
|
||||
}
|
||||
|
||||
.heading-gradient {
|
||||
@apply text-[#F4FFFA00] bg-clip-text bg-gradient-to-b from-foreground to-foreground/70;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user