mirror of
https://github.com/supabase/supabase.git
synced 2026-10-09 11:25:06 +03:00
Merge pull request #326 from supabase/feat/new_website
Feat/new website
This commit is contained in:
19 files changed
+870
-449
No files matched your search
@@ -32,14 +32,8 @@ const Button = (props: Props) => {
|
||||
className={`
|
||||
relative transition-all ${url ? textClass : ''}
|
||||
`}
|
||||
>
|
||||
{text}
|
||||
</span>
|
||||
{url && (
|
||||
<span className={`ml-2 transition-all opacity-0 group-hover:opacity-100 ${arrowClass}`}>
|
||||
→
|
||||
</span>
|
||||
)}
|
||||
>{text}</span>
|
||||
{url && (<span className={`ml-2 transition-all opacity-0 group-hover:opacity-100 ${arrowClass}`}>→</span>)}
|
||||
</button>
|
||||
)
|
||||
|
||||
|
||||
@@ -61,101 +61,111 @@ const Footer = (props: Props) => {
|
||||
Footer
|
||||
</h2>
|
||||
<div className="container mx-auto px-8 sm:px-16 xl:px-20 py-12 lg:pt-16 lg:pb-32">
|
||||
<div className="mx-auto max-w-7xl">
|
||||
<div className="xl:grid xl:grid-cols-3 xl:gap-8">
|
||||
<div className="space-y-8 xl:col-span-1">
|
||||
<img
|
||||
className="h-10 w-auto"
|
||||
src={darkMode ? `${basePath}/images/logo-dark.png` : `${basePath}/images/logo-light.png`}
|
||||
alt="Supabase"
|
||||
/>
|
||||
<div className="flex space-x-6">
|
||||
<a
|
||||
href="https://twitter.com/supabase_io"
|
||||
className="text-gray-300 hover:text-gray-400"
|
||||
>
|
||||
<span className="sr-only">Twitter</span>
|
||||
<svg className="h-6 w-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M8.29 20.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0022 5.92a8.19 8.19 0 01-2.357.646 4.118 4.118 0 001.804-2.27 8.224 8.224 0 01-2.605.996 4.107 4.107 0 00-6.993 3.743 11.65 11.65 0 01-8.457-4.287 4.106 4.106 0 001.27 5.477A4.072 4.072 0 012.8 9.713v.052a4.105 4.105 0 003.292 4.022 4.095 4.095 0 01-1.853.07 4.108 4.108 0 003.834 2.85A8.233 8.233 0 012 18.407a11.616 11.616 0 006.29 1.84" />
|
||||
</svg>
|
||||
</a>
|
||||
<div className="mx-auto max-w-7xl">
|
||||
<div className="xl:grid xl:grid-cols-3 xl:gap-8">
|
||||
<div className="space-y-8 xl:col-span-1">
|
||||
<img
|
||||
className="h-10 w-auto"
|
||||
src={darkMode ? 'images/logo-dark.png' : 'images/logo-light.png'}
|
||||
alt="Supabase"
|
||||
/>
|
||||
<div className="flex space-x-6">
|
||||
<a
|
||||
href="https://twitter.com/supabase_io"
|
||||
className="text-gray-300 hover:text-gray-400"
|
||||
>
|
||||
<span className="sr-only">Twitter</span>
|
||||
<svg
|
||||
className="h-6 w-6"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M8.29 20.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0022 5.92a8.19 8.19 0 01-2.357.646 4.118 4.118 0 001.804-2.27 8.224 8.224 0 01-2.605.996 4.107 4.107 0 00-6.993 3.743 11.65 11.65 0 01-8.457-4.287 4.106 4.106 0 001.27 5.477A4.072 4.072 0 012.8 9.713v.052a4.105 4.105 0 003.292 4.022 4.095 4.095 0 01-1.853.07 4.108 4.108 0 003.834 2.85A8.233 8.233 0 012 18.407a11.616 11.616 0 006.29 1.84" />
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<a href="https://github.com/supabase" className="text-gray-300 hover:text-gray-400">
|
||||
<span className="sr-only">GitHub</span>
|
||||
<svg className="h-6 w-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
<a href="https://github.com/supabase" className="text-gray-300 hover:text-gray-400">
|
||||
<span className="sr-only">GitHub</span>
|
||||
<svg
|
||||
className="h-6 w-6"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-12 grid grid-cols-1 gap-8 xl:mt-0 xl:col-span-2">
|
||||
<div className="grid grid-cols-2 gap-8 md:grid-cols-4">
|
||||
{FooterLinks.map((segment: any) => {
|
||||
return (
|
||||
<div key={`footer_${segment.title}`}>
|
||||
<h3 className="text-sm font-semibold text-gray-400 dark:text-dark-400 tracking-wider uppercase">
|
||||
{segment.title}
|
||||
</h3>
|
||||
<ul className="mt-4 space-y-4">
|
||||
{segment.links.map((link: any, idx: number) => (
|
||||
<li key={`${segment.title}_link_${idx}`}>
|
||||
<a
|
||||
href={link.url}
|
||||
className={`text-base ${
|
||||
link.url
|
||||
? 'text-gray-500 dark:text-dark-100'
|
||||
: 'text-gray-400 dark:text-dark-200'
|
||||
} hover:text-gray-900 dark:hover:text-gray-300`}
|
||||
>
|
||||
{link.text}
|
||||
{!link.url && (
|
||||
<span className="block text-sm text-gray-300 dark:text-dark-300">
|
||||
Coming soon
|
||||
</span>
|
||||
)}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-12 grid grid-cols-1 gap-8 xl:mt-0 xl:col-span-2">
|
||||
<div className="grid grid-cols-2 gap-8 md:grid-cols-4">
|
||||
{FooterLinks.map((segment: any) => {
|
||||
return (
|
||||
<div key={`footer_${segment.title}`}>
|
||||
<h3 className="text-sm font-semibold text-gray-400 dark:text-dark-400 tracking-wider uppercase">
|
||||
{segment.title}
|
||||
</h3>
|
||||
<ul className="mt-4 space-y-4">
|
||||
{segment.links.map((link: any, idx: number) => (
|
||||
<li key={`${segment.title}_link_${idx}`}>
|
||||
<a
|
||||
href={link.url}
|
||||
className={`text-base ${
|
||||
link.url
|
||||
? 'text-gray-500 dark:text-dark-100'
|
||||
: 'text-gray-400 dark:text-dark-200'
|
||||
} hover:text-gray-900 dark:hover:text-gray-300`}
|
||||
>
|
||||
{link.text}
|
||||
{!link.url && (
|
||||
<span className="block text-sm text-gray-300 dark:text-dark-300">
|
||||
Coming soon
|
||||
</span>
|
||||
)}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-12 border-t border-gray-200 dark:border-dark-600 pt-8 flex justify-between">
|
||||
<p className="text-base text-gray-400 dark:text-dark-400">© Supabase Inc</p>
|
||||
<div className="flex items-center">
|
||||
<SunEmoji />
|
||||
<button
|
||||
type="button"
|
||||
aria-pressed="false"
|
||||
className={`
|
||||
<div className="mt-12 border-t border-gray-200 dark:border-dark-600 pt-8 flex justify-between">
|
||||
<p className="text-base text-gray-400 dark:text-dark-400">© Supabase Inc</p>
|
||||
<div className="flex items-center">
|
||||
<SunEmoji />
|
||||
<button
|
||||
type="button"
|
||||
aria-pressed="false"
|
||||
className={`
|
||||
relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer
|
||||
transition-colors ease-in-out duration-200 focus:outline-none ${
|
||||
darkMode ? 'bg-dark-500' : 'bg-gray-200'
|
||||
} mx-5
|
||||
`}
|
||||
onClick={() => toggleDarkMode()}
|
||||
>
|
||||
<span className="sr-only">Toggle Themes</span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className={`
|
||||
onClick={() => toggleDarkMode()}
|
||||
>
|
||||
<span className="sr-only">Toggle Themes</span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className={`
|
||||
${darkMode ? 'translate-x-5' : 'translate-x-0'} inline-block h-5 w-5 rounded-full
|
||||
bg-white dark:bg-dark-700 shadow-lg transform ring-0 transition ease-in-out duration-200
|
||||
`}
|
||||
/>
|
||||
</button>
|
||||
<MoonEmoji />
|
||||
/>
|
||||
</button>
|
||||
<MoonEmoji />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -67,11 +67,6 @@ const Hero = () => {
|
||||
src={`${basePath}/images/logos/mozilla--grey.png`}
|
||||
alt="Mozilla"
|
||||
/>
|
||||
<img
|
||||
className="h-3 sm:h-5 pr-10"
|
||||
src={`${basePath}/images/logos/coatue.png`}
|
||||
alt="Coatue"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -83,7 +78,10 @@ const Hero = () => {
|
||||
<div className="h-2 w-2 mr-2 rounded-full bg-dark-500" />
|
||||
<div className="h-2 w-2 mr-2 rounded-full bg-dark-500" />
|
||||
</div>
|
||||
<div className="w-full relative bg-dark-900 pointer-events-none" style={{ padding: '56.25% 0 0 0' }}>
|
||||
<div
|
||||
className="w-full relative bg-dark-900 pointer-events-none"
|
||||
style={{ padding: '56.25% 0 0 0' }}
|
||||
>
|
||||
<iframe
|
||||
className="absolute w-full h-full rounded-b-md"
|
||||
src="https://www.youtube.com/embed/dBOSUER_5T4?playlist=dBOSUER_5T4&autoplay=1&loop=1&controls=0&modestbranding=1&rel=0&disablekb=1&mute=1&muted=1"
|
||||
|
||||
@@ -36,10 +36,10 @@ const MadeForDevelopers = () => {
|
||||
return (
|
||||
<div className={blockClass} key={`dev_feature_${idx}`}>
|
||||
<div className="lg:mt-5">
|
||||
<dt className="text-lg leading-6 font-medium text-gray-900 dark:text-white flex md:flex-col xl:flex-row">
|
||||
<dt className="text-lg leading-6 font-medium text-gray-900 dark:text-white flex flex-row sm:flex-col xl:flex-row">
|
||||
{feature.name}
|
||||
{feature.badge && (
|
||||
<Badge className="ml-3 md:ml-0 md:w-20 md:mt-2 xl:ml-3 xl:mt-0 xl:w-auto">
|
||||
<Badge className="ml-3 sm:ml-0 sm:w-20 sm:mt-2 xl:ml-3 xl:mt-0 xl:w-auto">
|
||||
{feature.badge}
|
||||
</Badge>
|
||||
)}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
import React from 'react'
|
||||
import CompanyData from 'data/Company.json'
|
||||
import BlogPostsData from 'data/BlogPosts.json'
|
||||
|
||||
type Props = {
|
||||
text: string
|
||||
description? : string
|
||||
url? : string
|
||||
icon? : string
|
||||
}
|
||||
|
||||
const Company = () => {
|
||||
const iconSections = Object.values(CompanyData).map((company : Props) => {
|
||||
const { text, description, url, icon } = company
|
||||
|
||||
const content = (
|
||||
<a
|
||||
href="#"
|
||||
className="-m-3 p-3 flex items-start rounded-lg hover:bg-gray-50 dark:hover:bg-dark-700 transition ease-in-out duration-150"
|
||||
>
|
||||
{/* <!-- Heroicon name: support --> */}
|
||||
<svg
|
||||
className="flex-shrink-0 h-6 w-6 stroke-gray dark:stroke-white "
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d={icon}
|
||||
/>
|
||||
</svg>
|
||||
<div className="ml-4">
|
||||
<p className="text-base font-medium text-gray-900 dark:text-white">{text}</p>
|
||||
<p className="mt-1 text-sm text-gray-500 dark:text-dark-300">{description}</p>
|
||||
</div>
|
||||
</a>
|
||||
)
|
||||
return (
|
||||
<a
|
||||
key={text}
|
||||
href={url}
|
||||
className="-m-3 p-3 flex flex-col justify-between rounded-lg transition ease-in-out duration-150"
|
||||
>
|
||||
{content}
|
||||
</a>
|
||||
)
|
||||
})
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<nav
|
||||
className="grid gap-y-10 px-4 py-8 sm:grid-cols-2 sm:gap-x-8 sm:py-12 sm:px-6 lg:px-8 xl:pr-12"
|
||||
aria-labelledby="solutionsHeading"
|
||||
>
|
||||
{iconSections}
|
||||
</nav>
|
||||
<div className="px-4 py-8 sm:py-12 sm:px-6 lg:px-8 xl:pl-12">
|
||||
<div>
|
||||
<h3 className="text-sm font-medium tracking-wide text-gray-500 dark:text-dark-300 uppercase">
|
||||
Latest blog posts
|
||||
</h3>
|
||||
<ul className="mt-6 space-y-6">
|
||||
{BlogPostsData.map((caseStudy: any, idx: number) => (
|
||||
<li className="flow-root" key={`flyout_case_${idx}`}>
|
||||
<a
|
||||
href={caseStudy.url}
|
||||
className="-m-3 py-3 flex rounded-lg hover:bg-gray-100 dark:hover:bg-dark-600 transition ease-in-out duration-150"
|
||||
>
|
||||
<div className="min-w-0 flex-1 sm:ml-3">
|
||||
<h4 className="text-base font-medium text-gray-900 dark:text-white truncate">
|
||||
{caseStudy.title}
|
||||
</h4>
|
||||
<p className="mt-1 text-sm text-gray-500 dark:text-dark-300">
|
||||
{caseStudy.description}
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="mt-6 text-sm font-medium">
|
||||
<a
|
||||
href="https://supabase.io/blog"
|
||||
className="text-gray-600 hover:text-gray-500 dark:text-brand-600 dark:hover:text-brand-700 transition ease-in-out duration-150"
|
||||
>
|
||||
View all posts <span aria-hidden="true">→</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
export default Company
|
||||
@@ -0,0 +1,107 @@
|
||||
import React from 'react'
|
||||
import DevelopersData from 'data/Developers.json'
|
||||
import AnnouncementsData from 'data/Announcements.json'
|
||||
|
||||
type Props = {
|
||||
text: string
|
||||
description? : string
|
||||
url? : string
|
||||
icon? : string
|
||||
}
|
||||
|
||||
|
||||
const Developers = () => {
|
||||
const iconSections = Object.values(DevelopersData).map((company : Props) => {
|
||||
const { text, description, url, icon } = company
|
||||
|
||||
const content = (
|
||||
<a
|
||||
href="#"
|
||||
className="-m-3 p-3 flex items-start rounded-lg hover:bg-gray-50 dark:hover:bg-dark-700 transition ease-in-out duration-150"
|
||||
>
|
||||
{/* <!-- Heroicon name: support --> */}
|
||||
<svg
|
||||
className="flex-shrink-0 h-6 w-6 stroke-gray dark:stroke-white "
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d={icon}
|
||||
/>
|
||||
</svg>
|
||||
<div className="ml-4">
|
||||
<p className="text-base font-medium text-gray-900 dark:text-white">{text}</p>
|
||||
<p className="mt-1 text-sm text-gray-500 dark:text-dark-300">{description}</p>
|
||||
</div>
|
||||
</a>
|
||||
)
|
||||
return url ? (
|
||||
<a
|
||||
key={text}
|
||||
href="#"
|
||||
className="-m-3 p-3 flex flex-col justify-between rounded-lg transition ease-in-out duration-150"
|
||||
>
|
||||
{content}
|
||||
</a>
|
||||
) : (
|
||||
<div
|
||||
key={text}
|
||||
className="-m-3 p-3 flex flex-col justify-between rounded-lg transition ease-in-out duration-150"
|
||||
>
|
||||
{content}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<nav
|
||||
className="grid gap-y-10 px-4 py-8 sm:grid-cols-2 sm:gap-x-8 sm:py-12 sm:px-6 lg:px-8 xl:pr-12"
|
||||
aria-labelledby="solutionsHeading"
|
||||
>
|
||||
{iconSections}
|
||||
</nav>
|
||||
<div className="px-4 py-8 sm:py-12 sm:px-6 lg:px-8 xl:pl-12">
|
||||
<div>
|
||||
<h3 className="text-sm font-medium tracking-wide text-gray-500 dark:text-dark-300 uppercase">
|
||||
Latest announcements
|
||||
</h3>
|
||||
<ul className="mt-6 space-y-16">
|
||||
{AnnouncementsData.map((caseStudy: any, idx: number) => (
|
||||
<li className="flow-root" key={`flyout_case_${idx}`}>
|
||||
<a
|
||||
href={caseStudy.url}
|
||||
className="-m-3 p-3 flex rounded-lg hover:bg-gray-100 dark:hover:bg-dark-600 transition ease-in-out duration-150"
|
||||
>
|
||||
<div className="hidden sm:block flex-shrink-0">
|
||||
<img
|
||||
className="w-32 h-20 object-cover rounded-md"
|
||||
src={caseStudy.imgUrl}
|
||||
alt="caseStudyThumb"
|
||||
/>
|
||||
</div>
|
||||
<div className="min-w-0 flex-1 sm:ml-8">
|
||||
<h4 className="text-base font-medium text-gray-900 dark:text-white truncate">
|
||||
{caseStudy.title}
|
||||
</h4>
|
||||
<p className="mt-1 text-sm text-gray-500 dark:text-dark-300">
|
||||
{caseStudy.description}
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
export default Developers
|
||||
@@ -1,194 +0,0 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import Badge from 'components/Badge'
|
||||
import Transition from 'lib/Transition'
|
||||
import Solutions from 'data/Solutions.json'
|
||||
import CaseStudiesData from 'data/CaseStudies.json'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
const FlyOut = () => {
|
||||
const { basePath } = useRouter()
|
||||
const [show, setShow] = useState(false)
|
||||
|
||||
function handleToggle() {
|
||||
setShow(!show)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// window is accessible here.
|
||||
window.addEventListener('scroll', function (e) {
|
||||
// close Fly Out window if user scrolls past 96px from top
|
||||
if (window.pageYOffset > 96) {
|
||||
setShow(false)
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
|
||||
const iconSections = Object.values(Solutions).map((solution) => {
|
||||
const { name, description, icon, label, url } = solution
|
||||
|
||||
const content = (
|
||||
<div className="flex md:h-full lg:flex-col">
|
||||
<div className="flex-shrink-0">
|
||||
<div className="inline-flex items-center justify-center h-10 w-10 rounded-md bg-gray-900 dark:bg-white text-white sm:h-12 sm:w-12">
|
||||
<svg
|
||||
className="h-6 w-6 stroke-white dark:stroke-black"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d={icon} />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div className="ml-4 md:flex-1 md:flex md:flex-col md:justify-between lg:ml-0 lg:mt-4">
|
||||
<div>
|
||||
<p className="text-base font-medium text-gray-900 dark:text-white">
|
||||
{name} {label && <Badge className="ml-3">{label}</Badge>}
|
||||
</p>
|
||||
<p className="mt-1 text-sm text-gray-500 dark:text-dark-300">{description}</p>
|
||||
</div>
|
||||
{url && (
|
||||
<p className="mt-2 text-sm font-medium text-brand-600 lg:mt-4">
|
||||
Learn more <span aria-hidden="true">→</span>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
return url ? (
|
||||
<a
|
||||
key={name}
|
||||
href="#"
|
||||
className="-m-3 p-3 flex flex-col justify-between rounded-lg hover:bg-gray-50 dark:hover:bg-dark-700 transition ease-in-out duration-150"
|
||||
>
|
||||
{content}
|
||||
</a>
|
||||
) : (
|
||||
<div
|
||||
key={name}
|
||||
className="-m-3 p-3 flex flex-col justify-between rounded-lg transition ease-in-out duration-150"
|
||||
>
|
||||
{content}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
<a
|
||||
href="#"
|
||||
className="inline-flex items-center px-1 border-b-2 border-transparent text-sm font-medium text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-dark-100 dark:hover:border-dark-100"
|
||||
onClick={() => handleToggle()}
|
||||
>
|
||||
<span>Product</span>
|
||||
<svg
|
||||
className="ml-2 h-5 w-5 text-gray-300 group-hover:text-gray-300 transition ease-in-out duration-150"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
<Transition
|
||||
appear={true}
|
||||
show={show}
|
||||
enter="transition ease-out duration-200"
|
||||
enterFrom="opacity-0 translate-y-1"
|
||||
enterTo="opacity-100 translate-y-0"
|
||||
leave="transition ease-in duration-150"
|
||||
leaveFrom="opacity-100 translate-y-0"
|
||||
leaveTo="opacity-0 translate-y-1"
|
||||
>
|
||||
<>
|
||||
<div
|
||||
className="absolute inset-x-0 transform shadow-lg border-t border-gray-50 dark:border-dark-500"
|
||||
style={{
|
||||
zIndex: 999,
|
||||
position: 'absolute',
|
||||
width: '100vw',
|
||||
margin: '0 auto',
|
||||
marginTop: '64px',
|
||||
left: '-50vw',
|
||||
right: '-50vw',
|
||||
}}
|
||||
>
|
||||
<div className="absolute inset-0 flex sm:flex-col lg:flex-row" aria-hidden="true">
|
||||
<div className="bg-white dark:bg-dark-600 sm:w-full sm:h-1/2 lg:w-1/2 lg:h-full"></div>
|
||||
<div className="bg-gray-50 dark:bg-dark-500 sm:w-full sm:h-1/2 lg:w-1/2 lg:h-full"></div>
|
||||
{/* <div className="bg-gray-50 dark:bg-dark-300 md:hidden lg:block lg:w-1/2"></div> */}
|
||||
</div>
|
||||
<div className="relative max-w-7xl mx-auto grid grid-cols-1 lg:grid-cols-2">
|
||||
<nav
|
||||
className="grid gap-y-10 px-4 py-8 sm:grid-cols-2 sm:gap-x-8 sm:py-12 sm:px-6 lg:px-8 xl:pr-12"
|
||||
aria-labelledby="solutionsHeading"
|
||||
>
|
||||
{iconSections}
|
||||
</nav>
|
||||
<div className="px-4 py-8 sm:py-12 sm:px-6 lg:px-8 xl:pl-12">
|
||||
<div>
|
||||
<h3 className="text-sm font-medium tracking-wide text-gray-500 dark:text-dark-300 uppercase">
|
||||
Latest news
|
||||
</h3>
|
||||
<ul className="mt-6 space-y-6">
|
||||
{CaseStudiesData.map((caseStudy: any, idx: number) => (
|
||||
<li className="flow-root" key={`flyout_case_${idx}`}>
|
||||
<a
|
||||
href={caseStudy.url}
|
||||
className="-m-3 p-3 flex rounded-lg hover:bg-gray-100 dark:hover:bg-dark-600 transition ease-in-out duration-150"
|
||||
>
|
||||
<div className="hidden sm:block flex-shrink-0">
|
||||
<img
|
||||
className="w-32 h-20 object-cover rounded-md"
|
||||
src={`${basePath}/${caseStudy.imgUrl}`}
|
||||
alt="caseStudyThumb"
|
||||
/>
|
||||
</div>
|
||||
<div className="min-w-0 flex-1 sm:ml-8">
|
||||
<h4 className="text-base font-medium text-gray-900 dark:text-white truncate">
|
||||
{caseStudy.title}
|
||||
</h4>
|
||||
<p className="mt-1 text-sm text-gray-500 dark:text-dark-300">
|
||||
{caseStudy.description}
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="mt-6 text-sm font-medium">
|
||||
<a
|
||||
href="/blog"
|
||||
className="text-gray-600 hover:text-gray-500 dark:text-brand-600 dark:hover:text-brand-700 transition ease-in-out duration-150"
|
||||
>
|
||||
View all posts <span aria-hidden="true">→</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="fixed inset-0 transition-opacity"
|
||||
style={{
|
||||
zIndex: 100,
|
||||
marginLeft: 0,
|
||||
pointerEvents: 'visiblePainted',
|
||||
}}
|
||||
onClick={() => handleToggle()}
|
||||
>
|
||||
<div className="absolute inset-0 opacity-0"></div>
|
||||
</div>
|
||||
</>
|
||||
</Transition>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default FlyOut
|
||||
@@ -0,0 +1,111 @@
|
||||
import React from 'react'
|
||||
import Badge from 'components/Badge'
|
||||
import SolutionsData from 'data/Solutions.json'
|
||||
import CaseStudiesData from 'data/CaseStudies.json'
|
||||
|
||||
const Product = () => {
|
||||
const iconSections = Object.values(SolutionsData).map((solution) => {
|
||||
const { name, description, icon, label, url } = solution
|
||||
|
||||
const content = (
|
||||
<div className="flex md:h-full lg:flex-col">
|
||||
<div className="flex-shrink-0">
|
||||
<div className="inline-flex items-center justify-center h-10 w-10 rounded-md bg-gray-900 dark:bg-white text-white sm:h-12 sm:w-12">
|
||||
<svg
|
||||
className="h-6 w-6 stroke-white dark:stroke-black"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d={icon} />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div className="ml-4 md:flex-1 md:flex md:flex-col md:justify-between lg:ml-0 lg:mt-4">
|
||||
<div>
|
||||
<p className="text-base font-medium text-gray-900 dark:text-white">
|
||||
{name} {label && <Badge className="ml-3">{label}</Badge>}
|
||||
</p>
|
||||
<p className="mt-1 text-sm text-gray-500 dark:text-dark-300">{description}</p>
|
||||
</div>
|
||||
{url && (
|
||||
<p className="mt-2 text-sm font-medium text-brand-600 lg:mt-4">
|
||||
Learn more <span aria-hidden="true">→</span>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
return url ? (
|
||||
<a
|
||||
key={name}
|
||||
href="#"
|
||||
className="-m-3 p-3 flex flex-col justify-between rounded-lg hover:bg-gray-50 dark:hover:bg-dark-700 transition ease-in-out duration-150"
|
||||
>
|
||||
{content}
|
||||
</a>
|
||||
) : (
|
||||
<div
|
||||
key={name}
|
||||
className="-m-3 p-3 flex flex-col justify-between rounded-lg transition ease-in-out duration-150"
|
||||
>
|
||||
{content}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<nav
|
||||
className="grid gap-y-10 px-4 py-8 sm:grid-cols-2 sm:gap-x-8 sm:py-12 sm:px-6 lg:px-8 xl:pr-12"
|
||||
aria-labelledby="solutionsHeading"
|
||||
>
|
||||
{iconSections}
|
||||
</nav>
|
||||
<div className="px-4 py-8 sm:py-12 sm:px-6 lg:px-8 xl:pl-12">
|
||||
<div>
|
||||
<h3 className="text-sm font-medium tracking-wide text-gray-500 dark:text-dark-300 uppercase">
|
||||
Latest case studies
|
||||
</h3>
|
||||
<ul className="mt-6 space-y-6">
|
||||
{CaseStudiesData.map((caseStudy: any, idx: number) => (
|
||||
<li className="flow-root" key={`flyout_case_${idx}`}>
|
||||
<a
|
||||
href={caseStudy.url}
|
||||
className="-m-3 p-3 flex rounded-lg hover:bg-gray-100 dark:hover:bg-dark-600 transition ease-in-out duration-150"
|
||||
>
|
||||
<div className="hidden sm:block flex-shrink-0">
|
||||
<img
|
||||
className="w-32 h-20 object-cover rounded-md"
|
||||
src={caseStudy.imgUrl}
|
||||
alt="caseStudyThumb"
|
||||
/>
|
||||
</div>
|
||||
<div className="min-w-0 flex-1 sm:ml-8">
|
||||
<h4 className="text-base font-medium text-gray-900 dark:text-white truncate">
|
||||
{caseStudy.title}
|
||||
</h4>
|
||||
<p className="mt-1 text-sm text-gray-500 dark:text-dark-300">
|
||||
{caseStudy.description}
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
{/* <div className="mt-6 text-sm font-medium">
|
||||
<a
|
||||
href="/blog"
|
||||
className="text-gray-600 hover:text-gray-500 dark:text-brand-600 dark:hover:text-brand-700 transition ease-in-out duration-150"
|
||||
>
|
||||
View all posts <span aria-hidden="true">→</span>
|
||||
</a>
|
||||
</div> */}
|
||||
</div>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
export default Product
|
||||
@@ -3,9 +3,13 @@ import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
import Badge from 'components/Badge'
|
||||
import FlyOut from 'components/Nav/FlyOut'
|
||||
import FlyOut from '~/components/UI/FlyOut'
|
||||
import Transition from 'lib/Transition'
|
||||
import Solutions from 'data/Solutions.json'
|
||||
import SolutionsData from 'data/Solutions.json'
|
||||
|
||||
import Solutions from '~/components/Nav/Product'
|
||||
import Developers from './Developers'
|
||||
import Company from './Company'
|
||||
|
||||
type Props = {
|
||||
darkMode: boolean
|
||||
@@ -16,6 +20,10 @@ const Nav = (props: Props) => {
|
||||
const { darkMode } = props
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
const [openProduct, setOpenProduct] = useState(false)
|
||||
const [openDevelopers, setOpenDevelopers] = useState(false)
|
||||
const [openCompany, setOpenCompany] = useState(false)
|
||||
|
||||
React.useEffect(() => {
|
||||
if (open) {
|
||||
console.log('open if')
|
||||
@@ -26,7 +34,18 @@ const Nav = (props: Props) => {
|
||||
}
|
||||
}, [open])
|
||||
|
||||
const iconSections = Object.values(Solutions).map((solution: any, idx: number) => {
|
||||
function handleToggle(callback: any) {
|
||||
handleCancel()
|
||||
callback()
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
setOpenProduct(false)
|
||||
setOpenDevelopers(false)
|
||||
setOpenCompany(false)
|
||||
}
|
||||
|
||||
const iconSections = Object.values(SolutionsData).map((solution: any, idx: number) => {
|
||||
const { name, description, icon, label, url } = solution
|
||||
|
||||
const content = (
|
||||
@@ -129,6 +148,36 @@ const Nav = (props: Props) => {
|
||||
</div>
|
||||
)
|
||||
|
||||
const FlyOutNavButton = (props: any) => (
|
||||
<div
|
||||
className={`
|
||||
inline-flex items-center px-1 border-b-2 border-transparent text-sm font-medium text-gray-500 hover:text-gray-700
|
||||
dark:text-dark-100 cursor-pointer
|
||||
` + (props.active )}
|
||||
onClick={props.onClick}
|
||||
>
|
||||
<>
|
||||
<span>{props.title}</span>
|
||||
<svg
|
||||
className={
|
||||
'ml-2 h-5 w-5 text-gray-300 group-hover:text-gray-300 transition ease-in-out duration-150' +
|
||||
(props.active && ' transform rotate-180')
|
||||
}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</>
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<nav className="bg-white dark:bg-dark-700 z-50 sticky">
|
||||
<div className="lg:container mx-auto relative flex justify-between h-16 lg:px-10 xl:px-0">
|
||||
@@ -149,26 +198,40 @@ const Nav = (props: Props) => {
|
||||
/>
|
||||
</div>
|
||||
<div className="pl-4 hidden sm:ml-6 lg:flex sm:space-x-8">
|
||||
<FlyOut />
|
||||
<FlyOutNavButton
|
||||
title={'Product'}
|
||||
onClick={() => handleToggle(() => setOpenProduct(!openProduct))}
|
||||
active={openProduct}
|
||||
/>
|
||||
<FlyOutNavButton
|
||||
title={'Developers'}
|
||||
onClick={() => handleToggle(() => setOpenDevelopers(!openDevelopers))}
|
||||
active={openDevelopers}
|
||||
/>
|
||||
{/* <FlyOutNavButton
|
||||
title={'Company'}
|
||||
onClick={() => handleToggle(() => setOpenCompany(!openCompany))}
|
||||
active={openCompany}
|
||||
/> */}
|
||||
|
||||
<FlyOut open={openProduct} handleCancel={handleCancel}>
|
||||
<Solutions />
|
||||
</FlyOut>
|
||||
<FlyOut open={openDevelopers} handleCancel={handleCancel}>
|
||||
<Developers />
|
||||
</FlyOut>
|
||||
{/* <FlyOut open={openCompany} handleCancel={handleCancel}>
|
||||
<Company />
|
||||
</FlyOut> */}
|
||||
<a
|
||||
href="/docs"
|
||||
href="/beta"
|
||||
className={`
|
||||
inline-flex items-center px-1 border-b-2 border-transparent text-sm font-medium
|
||||
text-gray-500 hover:text-gray-700 hover:border-gray-500 p-5
|
||||
dark:text-dark-100 dark:hover:border-dark-100
|
||||
`}
|
||||
>
|
||||
Developers
|
||||
</a>
|
||||
<a
|
||||
href="/docs/guides/platform"
|
||||
className={`
|
||||
inline-flex items-center px-1 border-b-2 border-transparent text-sm font-medium
|
||||
text-gray-500 hover:text-gray-700 hover:border-gray-500 p-5
|
||||
dark:text-dark-100 dark:hover:border-dark-100
|
||||
`}
|
||||
>
|
||||
Company
|
||||
Beta
|
||||
</a>
|
||||
<a
|
||||
href="/docs/pricing"
|
||||
@@ -187,11 +250,9 @@ const Nav = (props: Props) => {
|
||||
href="https://app.supabase.io?auth=signup"
|
||||
className={`
|
||||
inline-flex items-center border-b-2 border-transparent text-sm font-normal transition
|
||||
rounded-md px-3 py-1 mr-5 bg-brand-600 text-white hover:bg-brand-700 hidden lg:block
|
||||
rounded-md px-3 py-1 mr-5 bg-brand-700 text-white hover:bg-brand-800 hidden lg:block
|
||||
`}
|
||||
>
|
||||
Start your project
|
||||
</a>
|
||||
>Start your project</a>
|
||||
<a
|
||||
href="https://app.supabase.io?auth=signin"
|
||||
className={`
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
import React, { useState, useEffect, useImperativeHandle, forwardRef } from 'react'
|
||||
import Transition from 'lib/Transition'
|
||||
|
||||
type Props = {
|
||||
title?: string
|
||||
children?: React.ReactNode
|
||||
className?: string
|
||||
open?: boolean
|
||||
handleCancel?: any
|
||||
singleBgColor?: boolean
|
||||
}
|
||||
|
||||
const FlyOut = (props: Props) => {
|
||||
const { title = '', children, className = '', singleBgColor = false, handleCancel } = props
|
||||
const [show, setShow] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
function handleScroll() {
|
||||
if (window.pageYOffset > 96) handleCancel()
|
||||
}
|
||||
window.addEventListener('scroll', handleScroll)
|
||||
return () => window.removeEventListener('scroll', handleScroll)
|
||||
})
|
||||
|
||||
|
||||
// function handleToggle() {
|
||||
// setShow(!show)
|
||||
// props.handleClick()
|
||||
// }
|
||||
|
||||
// useImperativeHandle(
|
||||
// ref,
|
||||
// () => ({
|
||||
// close() {
|
||||
// setShow(false)
|
||||
// }
|
||||
// }),
|
||||
// )
|
||||
|
||||
// useEffect(() => {
|
||||
// // window is accessible here.
|
||||
// window.addEventListener('scroll', function (e) {
|
||||
// // close Fly Out window if user scrolls past 96px from top
|
||||
// if (window.pageYOffset > 96) {
|
||||
// setShow(false)
|
||||
// }
|
||||
// })
|
||||
// }, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Transition
|
||||
appear={true}
|
||||
show={props.open}
|
||||
enter="transition ease-out duration-200"
|
||||
enterFrom="opacity-0 translate-y-1"
|
||||
enterTo="opacity-100 translate-y-0"
|
||||
leave="transition ease-in duration-150"
|
||||
leaveFrom="opacity-100 translate-y-0"
|
||||
leaveTo="opacity-0 translate-y-1"
|
||||
>
|
||||
<>
|
||||
<div
|
||||
className="absolute inset-x-0 transform shadow-lg border-t border-gray-100 dark:border-dark-500"
|
||||
style={{
|
||||
zIndex: 999,
|
||||
position: 'absolute',
|
||||
width: '100vw',
|
||||
margin: '0 auto',
|
||||
marginTop: '63px',
|
||||
left: '-50vw',
|
||||
right: '-50vw',
|
||||
top: 0,
|
||||
}}
|
||||
>
|
||||
<div className="absolute inset-0 flex sm:flex-col lg:flex-row" aria-hidden="true">
|
||||
<div className="bg-white dark:bg-dark-600 sm:w-full sm:h-1/2 lg:w-1/2 lg:h-full" />
|
||||
<div className={`${singleBgColor ? 'bg-white dark:bg-dark-600' : 'bg-gray-50 dark:bg-dark-500'} sm:w-full sm:h-1/2 lg:w-1/2 lg:h-full`} />
|
||||
{/* <div className="bg-gray-50 dark:bg-dark-300 md:hidden lg:block lg:w-1/2"></div> */}
|
||||
</div>
|
||||
<div
|
||||
className={`relative max-w-7xl mx-auto grid grid-cols-1 lg:grid-cols-2 ${className}`}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="fixed inset-0 t-63 transition-opacity bg-red"
|
||||
style={{
|
||||
zIndex: 10,
|
||||
top: '63px',
|
||||
marginLeft: 0,
|
||||
pointerEvents: 'visiblePainted',
|
||||
}}
|
||||
onClick={() => props.handleCancel()}
|
||||
>
|
||||
<div className="absolute inset-0 opacity-0"></div>
|
||||
</div>
|
||||
</>
|
||||
</Transition>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default FlyOut
|
||||
@@ -0,0 +1,18 @@
|
||||
[
|
||||
{
|
||||
"type": "Announcement",
|
||||
"title": "Beta launch",
|
||||
"description": "We're excitied to announce we have moved from Alpha to Beta",
|
||||
"imgUrl": "images/case-study-monitoro.jpg",
|
||||
"logoUrl": "images/logos/monitoro.png",
|
||||
"organization": "Monitoro",
|
||||
"url": "/casestudies/monitoro",
|
||||
"postMeta": {
|
||||
"name" : "Paul Copplestone",
|
||||
"avatarUrl": "https://avatars0.githubusercontent.com/u/10214025?v=4",
|
||||
"publishDate": "Mar 16, 2020",
|
||||
"readLength": 6
|
||||
},
|
||||
"ctaText": "View case study"
|
||||
}
|
||||
]
|
||||
+14
-7
@@ -163,15 +163,22 @@ export const IntroductionSegments = [
|
||||
},
|
||||
]
|
||||
|
||||
// Currently unused
|
||||
export const PerformanceComparisonData = [
|
||||
{
|
||||
name: 'Supabase',
|
||||
read: 1167,
|
||||
write: 870
|
||||
key: 'read',
|
||||
title: 'Read (requests/s)',
|
||||
stats: [
|
||||
{ name: 'Supabase', value: 1167, color: 'bg-brand-600 dark:bg-brand-700' },
|
||||
{ name: 'Firestore', value: 366, color: 'bg-dark-300 dark:bg-dark-400' },
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'Firestore',
|
||||
read: 366,
|
||||
write: 280
|
||||
key: 'write',
|
||||
title: 'Write (requests/s)',
|
||||
stats: [
|
||||
{ name: 'Supabase', value: 870, color: 'bg-brand-600 dark:bg-brand-700' },
|
||||
{ name: 'Firestore', value: 280, color: 'bg-dark-300 dark:bg-dark-400' },
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
@@ -0,0 +1,26 @@
|
||||
[
|
||||
{
|
||||
"type": "Blog post",
|
||||
"title": "Tayfa Built a No-Code Website Builder Deployed in Seven Days",
|
||||
"description": "",
|
||||
"postMeta": {
|
||||
"name" : "Paul Copplestone",
|
||||
"avatarUrl": "https://supabase.io/blog/2020/12/02/case-study-xendit",
|
||||
"publishDate": "",
|
||||
"readLength": 6
|
||||
},
|
||||
"ctaText": "View blog post"
|
||||
},
|
||||
{
|
||||
"type": "Blog post",
|
||||
"title": "Supabase Partners With Strive School To Help Teach Open Source",
|
||||
"description": "",
|
||||
"postMeta": {
|
||||
"name" : "Paul Copplestone",
|
||||
"avatarUrl": "https://supabase.io/blog/2020/12/02/supabase-striveschool",
|
||||
"publishDate": "",
|
||||
"readLength": 6
|
||||
},
|
||||
"ctaText": "View blog post"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,26 @@
|
||||
[
|
||||
{
|
||||
"text": "Blog",
|
||||
"description": "Read the latest developments from Supabase",
|
||||
"url": "https://supabase.io/blog",
|
||||
"icon": "M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m2 13a2 2 0 01-2-2V7m2 13a2 2 0 002-2V9a2 2 0 00-2-2h-2m-4-3H9M7 16h6M7 8h6v4H7V8z"
|
||||
},
|
||||
{
|
||||
"text": "Open Source",
|
||||
"description": "Supabase is an opensource company, supporting existing open source tools and communities wherever possible.",
|
||||
"url": "https://supabase.io/oss",
|
||||
"icon": "M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z"
|
||||
},
|
||||
{
|
||||
"text": "Humans.txt",
|
||||
"description": "Overview of Supabase staff",
|
||||
"url": "https://supabase.io/humans.txt",
|
||||
"icon": "M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"
|
||||
},
|
||||
{
|
||||
"text": "Lawyers.txt",
|
||||
"description": "Details of technology used by Supabase",
|
||||
"url": "https://supabase.io/lawyers.txt",
|
||||
"icon": "M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,14 @@
|
||||
[
|
||||
{
|
||||
"text": "Documentation",
|
||||
"description": "Everything you need that makes it simple to get started.",
|
||||
"url": "https://supabase.io/docs",
|
||||
"icon": "M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"
|
||||
},
|
||||
{
|
||||
"text": "API reference",
|
||||
"description": "Examples and references for using API libraries",
|
||||
"url": "https://supabase.io/docs/client/supabase-client",
|
||||
"icon": "M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"
|
||||
}
|
||||
]
|
||||
+169
-131
@@ -1,35 +1,82 @@
|
||||
import Head from 'next/head'
|
||||
import { useRef } from 'react'
|
||||
import { useRef, useState } from 'react'
|
||||
import Container from 'components/Container'
|
||||
import Layout from 'components/Layout'
|
||||
import CountUp from 'components/CountUp'
|
||||
import FlyOut from 'components/UI/FlyOut'
|
||||
import { APP_NAME, DESCRIPTION } from 'lib/constants'
|
||||
import { AlphaNumbers, IntroductionSegments } from 'data/BetaPage'
|
||||
import { useRouter } from 'next/router'
|
||||
import { AlphaNumbers, IntroductionSegments, PerformanceComparisonData } from 'data/BetaPage'
|
||||
import { render } from 'react-dom'
|
||||
|
||||
const site_title = `${APP_NAME} | We are now in Beta`
|
||||
|
||||
// Dark text: text-dark-400
|
||||
// Light text: text-dark-300
|
||||
|
||||
const HamburgerMenu = () => (
|
||||
<div className="cursor-pointer">
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
height="24"
|
||||
stroke="white"
|
||||
strokeWidth="2"
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<line x1="3" y1="12" x2="21" y2="12" />
|
||||
<line x1="3" y1="6" x2="21" y2="6" />
|
||||
<line x1="3" y1="18" x2="21" y2="18" />
|
||||
</svg>
|
||||
</div>
|
||||
)
|
||||
const NavFlyOutMenu = (props: any) => {
|
||||
const { scrollTo } = props
|
||||
const segments = IntroductionSegments.map((segment: any) => segment.chapters)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="col-span-12 grid grid-cols-12 items-center border-dark-300 dark:border-dark-400 sm:border-r border-b border-dashed">
|
||||
{segments.flat().map((segment: any) => (
|
||||
<div
|
||||
onClick={() => scrollTo(segment.key)}
|
||||
className={`
|
||||
col-span-12 lg:col-span-4 px-4 lg:px-6 py-4 lg:py-10 text-dark-300 dark:text-dark-400 cursor-pointer bg-white dark:bg-dark-600
|
||||
hover:bg-dark-100 dark:hover:bg-dark-500 border-dark-300 dark:border-dark-400 border-t sm:border-l border-dashed`}
|
||||
>
|
||||
<p className="flex items-center text-black dark:text-white">
|
||||
<span className="font-mono text-xs text-dark-300 dark:text-dark-400 mr-2">
|
||||
0{segment.no}
|
||||
</span>
|
||||
<span>{segment.name}</span>
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const VideoShot = (props: any) => {
|
||||
const { src } = props
|
||||
return (
|
||||
<div className="shadow-lg rounded-md" style={{ height: 'fit-content' }}>
|
||||
<div className="w-full rounded-t-md h-5 bg-dark-400 flex items-center justify-start px-2">
|
||||
<div className="h-2 w-2 mr-2 rounded-full bg-dark-500" />
|
||||
<div className="h-2 w-2 mr-2 rounded-full bg-dark-500" />
|
||||
<div className="h-2 w-2 mr-2 rounded-full bg-dark-500" />
|
||||
</div>
|
||||
<video className="rounded-b-md" src={src} autoPlay loop muted>
|
||||
Your browser does not support the video tag
|
||||
</video>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const HamburgerMenu = (props: any) => {
|
||||
const { openMenu } = props
|
||||
return (
|
||||
<div className="cursor-pointer" onClick={openMenu}>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
height="24"
|
||||
stroke="white"
|
||||
strokeWidth="2"
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<line x1="3" y1="12" x2="21" y2="12" />
|
||||
<line x1="3" y1="6" x2="21" y2="6" />
|
||||
<line x1="3" y1="18" x2="21" y2="18" />
|
||||
</svg>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const SectionHeader = (props: any) => {
|
||||
const { sectionNumber, header } = props
|
||||
@@ -44,14 +91,18 @@ const SectionHeader = (props: any) => {
|
||||
}
|
||||
|
||||
const Hero = () => (
|
||||
<div className="w-screen py-16 lg:py-36 bg-dark-800">
|
||||
<div className="container mx-auto px-8 lg:px-28 py-20 h-full grid grid-cols-12 gap-4 items-center text-dark-300">
|
||||
<div className="col-span-12 md:col-span-9 lg:col-span-8 xl:col-span-6 text-2xl text-white">
|
||||
<p className="mb-10">
|
||||
Supabase is an opensource Firebase alternative. We're building the features of Firebase
|
||||
using enterprise-grade, open source tools.
|
||||
<div
|
||||
style={{ backgroundImage: "url('images/beta-hero.png')", backgroundSize: "65%", backgroundPosition: "120% 50%"}}
|
||||
className="w-screen py-16 lg:py-36 bg-dark-800 bg-no-repeat"
|
||||
>
|
||||
<div
|
||||
className="container mx-auto px-8 lg:px-28 py-20 h-full grid grid-cols-12 gap-4 items-center text-dark-300"
|
||||
>
|
||||
<div className="col-span-12 md:col-span-9 lg:col-span-8 xl:col-span-6 text-white">
|
||||
<p className="mb-10 text-4xl">
|
||||
Supabase is an opensource Firebase alternative.
|
||||
</p>
|
||||
<p>
|
||||
<p className="text-2xl">
|
||||
Today, we're moving to <span className="text-brand-700">Beta</span>
|
||||
</p>
|
||||
</div>
|
||||
@@ -59,23 +110,29 @@ const Hero = () => (
|
||||
</div>
|
||||
)
|
||||
|
||||
const Introduction = (props: any) => {
|
||||
const { scrollTo } = props
|
||||
const Introduction = () => {
|
||||
return (
|
||||
<div className="bg-gray-50 dark:bg-dark-700">
|
||||
<div className="container mx-auto px-8 lg:px-28 py-20 grid grid-cols-12 gap-4 text-dark-400 dark:text-dark-300">
|
||||
<div className="col-span-12 sm:col-span-9 xl:col-span-7 text-base mb-20">
|
||||
<div className="bg-dark-900">
|
||||
<div className="container mx-auto px-8 lg:px-28 py-20 grid grid-cols-12 gap-4 text-dark-300">
|
||||
<div className="col-span-12 sm:col-span-9 xl:col-span-8">
|
||||
<p>
|
||||
After the launch of our{' '}
|
||||
<a
|
||||
href="https://news.ycombinator.com/item?id=23319901"
|
||||
target="_blank"
|
||||
className="text-brand-700 hover:text-brand-800"
|
||||
>
|
||||
Alpha
|
||||
</a>{' '}
|
||||
Program in June, we've been fortunate to work with thousands of early adopters on
|
||||
improving both our Open Source, and Hosted offerings.
|
||||
<span className="text-2xl block text-white">
|
||||
After the launch of our{' '}
|
||||
<a
|
||||
href="https://news.ycombinator.com/item?id=23319901"
|
||||
target="_blank"
|
||||
className="text-brand-700 hover:text-brand-800"
|
||||
>
|
||||
Alpha
|
||||
</a>{' '}
|
||||
Program in June,
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div className="col-span-12 sm:col-span-9 xl:col-span-6 text-base mb-20">
|
||||
<p>
|
||||
we've been fortunate to work with thousands of early adopters on improving both our Open
|
||||
Source, and Hosted offerings.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -108,7 +165,21 @@ const Introduction = (props: any) => {
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const TableOfContents = (props: any) => {
|
||||
const { scrollTo } = props
|
||||
return (
|
||||
<div className="bg-gray-50 dark:bg-dark-700 text-dark-400 dark:text-dark-300">
|
||||
<div className="container mx-auto px-8 lg:px-28 py-20 grid grid-cols-12 gap-4">
|
||||
<div className="col-span-12 text-base mb-10">
|
||||
<p className="text-2xl text-black dark:text-white">
|
||||
Supabase <span className="text-brand-600 dark:text-brand-700">Beta</span> is starting now.
|
||||
</p>
|
||||
</div>
|
||||
<div className="col-span-12 grid grid-cols-12 gap-y-10">
|
||||
{IntroductionSegments.map((segment: any, segmentIdx: number) => (
|
||||
<div key={`introSegment_${segmentIdx}`} className="col-span-12 grid grid-cols-12">
|
||||
@@ -123,10 +194,10 @@ const Introduction = (props: any) => {
|
||||
className="flex items-center mb-5 cursor-pointer"
|
||||
onClick={() => scrollTo(chapter.key)}
|
||||
>
|
||||
<p className="font-mono text-xs text-dark-300 dark:text-dark-400">{`0${
|
||||
chapter.no
|
||||
}`}</p>
|
||||
<p className="ml-4 transition text-base border-b border-gray-400 hover:text-black">{chapter.name}</p>
|
||||
<p className="font-mono text-xs text-dark-300 dark:text-dark-400">{`0${chapter.no}`}</p>
|
||||
<p className="ml-4 transition text-base border-b border-gray-400 hover:text-black">
|
||||
{chapter.name}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -139,85 +210,36 @@ const Introduction = (props: any) => {
|
||||
}
|
||||
|
||||
const Performance = () => {
|
||||
|
||||
const ComparisonChart = () => {
|
||||
const maxValue = 1600
|
||||
return (
|
||||
<div className="grid grid-cols-12 text-dark-400 dark:text-dark-300 items-center">
|
||||
<div className="col-span-12">
|
||||
<p className="w-36 pb-2 mb-4">Read (requests/s)</p>
|
||||
</div>
|
||||
<div className="col-span-12 sm:col-span-10 grid grid-cols-12">
|
||||
<div className="col-span-12 grid grid-cols-12 items-center">
|
||||
<div className="col-span-4 sm:col-span-2 py-2 border-r border-dark-200 dark:border-dark-400 mr-3">
|
||||
Supabase
|
||||
</div>
|
||||
<div className="col-span-9 py-2">
|
||||
<div
|
||||
className="bg-brand-600 dark:bg-brand-700 transition-all flex items-center justify-center group rounded-full h-3 hover:h-6"
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
<p className="transition text-dark-700 opacity-0 group-hover:opacity-100">1167 requests/s</p>
|
||||
<div>
|
||||
{PerformanceComparisonData.map((metric: any) => {
|
||||
const multiplier = (metric.stats[0].value / metric.stats[1].value).toFixed(1)
|
||||
return (
|
||||
<div key={`${metric.key}`} className="mb-10 text-dark-400 dark:text-dark-300">
|
||||
<p className="sm:w-36 pb-2 mb-4">{metric.title}</p>
|
||||
<div className="flex flex-col sm:flex-row sm:items-center">
|
||||
<div className="w-full sm:w-5/6">
|
||||
{metric.stats.map((stat: any, idx: number) => (
|
||||
<div key={`metric_${metric.key}_${idx}`} className="flex items-center">
|
||||
<p className="w-20 lg:w-24 border-r py-2 pr-4 mr-4 text-left sm:text-right">{stat.name}</p>
|
||||
<div
|
||||
className={`${stat.color} rounded-full h-3 transition-all`}
|
||||
style={{ width: `calc(${((stat.value / maxValue) * 100)}%)`}}
|
||||
/>
|
||||
<p className="ml-2">{stat.value}/s</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="text-left sm:w-1/6 sm:text-right flex flex-col">
|
||||
<p className="text-6xl text-dark-700 dark:text-dark-100">{multiplier}x</p>
|
||||
<p className="text-sm -mt-2">more {metric.key}s per second</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="hidden col-span-1 sm:block" />
|
||||
</div>
|
||||
<div className="col-span-12 grid grid-cols-12 items-center">
|
||||
<div className="col-span-4 sm:col-span-2 py-2 border-r border-dark-200 dark:border-dark-400 mr-3">
|
||||
Firestore
|
||||
</div>
|
||||
<div className="col-span-9 py-2">
|
||||
<div
|
||||
className="bg-dark-300 dark:bg-dark-400 transition-all flex items-center justify-center group rounded-full h-3 hover:h-6"
|
||||
style={{ width: '31.36%' }}
|
||||
>
|
||||
<p className="transition text-dark-700 opacity-0 group-hover:opacity-100">366 requests/s</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-12 sm:col-span-2">
|
||||
<p className="text-6xl text-dark-700 sm:text-right">3.2x</p>
|
||||
<p className="text-sm sm:text-right -mt-2">faster read requests</p>
|
||||
</div>
|
||||
|
||||
<div className="col-span-12 py-5" />
|
||||
|
||||
<div className="col-span-12">
|
||||
<p className="w-36 pb-2 mb-4">Write (requests/s)</p>
|
||||
</div>
|
||||
<div className="col-span-12 sm:col-span-10 grid grid-cols-12">
|
||||
<div className="col-span-12 grid grid-cols-12 items-center">
|
||||
<div className="col-span-4 sm:col-span-2 py-2 border-r border-dark-200 dark:border-dark-400 mr-3">
|
||||
Supabase
|
||||
</div>
|
||||
<div className="col-span-9 py-2">
|
||||
<div
|
||||
className="bg-brand-600 dark:bg-brand-700 transition-all flex items-center justify-center group rounded-full h-3 hover:h-6"
|
||||
style={{ width: '74.55%' }}
|
||||
>
|
||||
<p className="transition text-dark-700 opacity-0 group-hover:opacity-100">870 requests/s</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="hidden col-span-1 sm:block" />
|
||||
</div>
|
||||
<div className="col-span-12 grid grid-cols-12 items-center">
|
||||
<div className="col-span-4 sm:col-span-2 py-2 border-r border-dark-200 dark:border-dark-400 mr-3">
|
||||
Firestore
|
||||
</div>
|
||||
<div className="col-span-9 py-2">
|
||||
<div
|
||||
className="bg-dark-300 dark:bg-dark-400 transition-all flex items-center justify-center group rounded-full h-3 hover:h-6"
|
||||
style={{ width: '23.99%' }}
|
||||
>
|
||||
<p className="transition text-dark-700 opacity-0 group-hover:opacity-100">280 requests/s</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-12 sm:col-span-2">
|
||||
<p className="text-6xl text-dark-700 sm:text-right">3.1x</p>
|
||||
<p className="text-sm sm:text-right -mt-2">faster write requests</p>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -395,8 +417,10 @@ const Reliability = () => (
|
||||
https://status.supabase.io
|
||||
</a>{' '}
|
||||
to keep track of uptime across all of our services and critical infrastructure.
|
||||
(Possibly drop a screen grab here)
|
||||
</p>
|
||||
<div className="mb-10">
|
||||
<VideoShot src="videos/statusPage.mp4" />
|
||||
</div>
|
||||
<p>
|
||||
For our Alpha & Beta Users we take daily backups of your Database free of charge up to
|
||||
20GB and store them in an encrypted format. They are available to download at any time
|
||||
@@ -422,7 +446,7 @@ const NewFeaturesAndIntegrations = () => (
|
||||
<ul className="">
|
||||
<li className="mb-10">
|
||||
<p className="w-20 pb-2 mb-2 border-b-2 border-dark-200 dark:border-dark-400">Auth</p>
|
||||
<p>
|
||||
<p className="mb-5">
|
||||
We provide an easy to use{' '}
|
||||
<a
|
||||
href="https://supabase.io/docs/client/auth-signup"
|
||||
@@ -447,6 +471,7 @@ const NewFeaturesAndIntegrations = () => (
|
||||
can use to log them into your app, and even offer several OAuth providers including
|
||||
Google, GitHub, and have more on the way.
|
||||
</p>
|
||||
<VideoShot src="videos/tabAuthRules.mp4" />
|
||||
</li>
|
||||
<li className="mb-10">
|
||||
<p className="w-20 pb-2 mb-2 border-b-2 border-dark-200 dark:border-dark-400">
|
||||
@@ -505,19 +530,21 @@ const NewFeaturesAndIntegrations = () => (
|
||||
<p className="w-24 pb-2 mb-2 border-b-2 border-dark-200 dark:border-dark-400">
|
||||
Table View
|
||||
</p>
|
||||
<p>
|
||||
<p className="mb-5">
|
||||
View and edit your data like a spreadsheet from within the Supabase dashboard,
|
||||
create complex relationships, import and export to csv.
|
||||
</p>
|
||||
<VideoShot src="videos/tabTableEditor.mp4" />
|
||||
</li>
|
||||
<li>
|
||||
<p className="w-24 pb-2 mb-2 border-b-2 border-dark-200 dark:border-dark-400">
|
||||
SQL Editor
|
||||
</p>
|
||||
<p>
|
||||
<p className="mb-5">
|
||||
No need to install third party SQL tools, you can run queries directly from the
|
||||
Supabase Dashbaord.
|
||||
</p>
|
||||
<VideoShot src="videos/tabSqlEditor.mp4" />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -742,7 +769,9 @@ const WhatsNext = () => (
|
||||
)
|
||||
|
||||
const Beta = () => {
|
||||
const { basePath } = useRouter()
|
||||
|
||||
const [ menuOpen, setMenuOpen ] = useState<boolean>(false)
|
||||
|
||||
const references: any = {
|
||||
performance: useRef<HTMLDivElement>(null),
|
||||
security: useRef<HTMLDivElement>(null),
|
||||
@@ -781,12 +810,21 @@ const Beta = () => {
|
||||
<meta name="twitter:image" content="/public/og/og-image.jpg" />
|
||||
</Head>
|
||||
<Container>
|
||||
<div className="shadow-lg py-5 px-5 lg:px-20 sticky inset-0 bg-dark-800 z-50 flex items-center justify-between">
|
||||
<img className="h-5" src={`${basePath}/images/logo-dark.png`} />
|
||||
<HamburgerMenu />
|
||||
<div className="relative shadow-lg py-5 px-5 xl:px-20 sticky inset-0 bg-dark-800 z-50 flex items-center justify-between">
|
||||
<img className="h-5" src="images/logo-dark.png" />
|
||||
<HamburgerMenu openMenu={() => setMenuOpen(!menuOpen)}/>
|
||||
<FlyOut
|
||||
open={menuOpen}
|
||||
handleCancel={() => setMenuOpen(false)}
|
||||
className="lg:col-span-1"
|
||||
singleBgColor={true}
|
||||
>
|
||||
<NavFlyOutMenu scrollTo={scrollTo} />
|
||||
</FlyOut>
|
||||
</div>
|
||||
<Hero />
|
||||
<Introduction scrollTo={scrollTo} />
|
||||
<Introduction />
|
||||
<TableOfContents scrollTo={scrollTo} />
|
||||
<div ref={references['performance']}>
|
||||
<Performance />
|
||||
</div>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 139 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 61 KiB |
Binary file not shown.
Reference in new issue
Block a user