diff --git a/apps/www/components/BuiltWithSupabase.tsx b/apps/www/components/BuiltWithSupabase.tsx deleted file mode 100644 index 5059b7a9e9d..00000000000 --- a/apps/www/components/BuiltWithSupabase.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { Button, IconGitHubSolid } from 'ui' -import Link from 'next/link' - -import SectionContainer from '~/components/Layouts/SectionContainer' -import ExampleCard from './ExampleCard' - -import Examples from 'data/Examples' - -const BuiltWithSupabase = () => { - return ( - -
-

Start building in seconds

-

- Kickstart your next project with templates built by us and our community. -

-
- - -
-
-
- {Examples.slice(0, 6).map((example, i) => { - return ( -
2 && `sm:hidden lg:block`}`} - key={i} - > - -
- ) - })} -
-
- ) -} - -export default BuiltWithSupabase diff --git a/apps/www/components/BuiltWithSupabase/ExamplesMobile.tsx b/apps/www/components/BuiltWithSupabase/ExamplesMobile.tsx new file mode 100644 index 00000000000..130f54fdf5c --- /dev/null +++ b/apps/www/components/BuiltWithSupabase/ExamplesMobile.tsx @@ -0,0 +1,53 @@ +import 'swiper/css' + +import React, { FC } from 'react' +import { useRouter } from 'next/router' +import { Swiper, SwiperSlide } from 'swiper/react' +import ExampleCard from '../ExampleCard' + +import content from '~/data/home/content' +import type { Example } from 'data/Examples' + +interface Props { + examples: Example[] + className?: string +} + +const ExamplesMobile: FC = ({ examples, className }: any) => { + const { basePath } = useRouter() + + return ( +
+ + {examples.map((example: Example, i: number) => ( + + + + ))} + +
+ ) +} + +export default ExamplesMobile diff --git a/apps/www/components/BuiltWithSupabase/index.tsx b/apps/www/components/BuiltWithSupabase/index.tsx new file mode 100644 index 00000000000..61971b8fa03 --- /dev/null +++ b/apps/www/components/BuiltWithSupabase/index.tsx @@ -0,0 +1,58 @@ +import { Button, cn, IconGitHubSolid } from 'ui' +import Link from 'next/link' + +import SectionContainer from '~/components/Layouts/SectionContainer' +import ExampleCard from '../ExampleCard' + +import Examples from 'data/Examples' +import ExamplesMobile from './ExamplesMobile' + +const BuiltWithSupabase = () => { + return ( + <> + +
+

Start building in seconds

+

+ Kickstart your next project with templates built by us and our community. +

+
+ + +
+
+
+ + +
+ {Examples.slice(0, 6).map((example, i) => { + return ( +
+ +
+ ) + })} +
+
+ + ) +} + +export default BuiltWithSupabase diff --git a/apps/www/components/CustomerStories/CustomersSliderMobile.tsx b/apps/www/components/CustomerStories/CustomersSliderMobile.tsx new file mode 100644 index 00000000000..5c3cedd2f04 --- /dev/null +++ b/apps/www/components/CustomerStories/CustomersSliderMobile.tsx @@ -0,0 +1,48 @@ +import 'swiper/css' +import React from 'react' +import { Swiper, SwiperSlide } from 'swiper/react' + +import { cn } from 'ui' +import { CompositionCol } from '.' +import type { CompositionColType } from '.' + +interface Props { + className?: string + columns: CompositionColType[] +} + +const CustomersSliderMobile: React.FC = ({ columns, className }) => ( +
+ + {columns.map((column: CompositionColType, i: number) => ( + + + + ))} + +
+) + +export default CustomersSliderMobile diff --git a/apps/www/components/CustomerStories/CutomsersSliderDesktop.tsx b/apps/www/components/CustomerStories/CutomsersSliderDesktop.tsx new file mode 100644 index 00000000000..4f0a60d71cb --- /dev/null +++ b/apps/www/components/CustomerStories/CutomsersSliderDesktop.tsx @@ -0,0 +1,52 @@ +import React from 'react' +import { range } from 'lodash' +import { cn } from 'ui' + +import { CompositionCol } from '.' +import type { CompositionColType } from '.' + +interface Props { + className?: string + columns: CompositionColType[] +} + +const compositionGap = 'gap-4' + +const CutomsersSliderDesktop: React.FC = ({ columns, className }) => ( +
+ {range(0, 2).map((_, idx1: number) => ( +
+ {columns.map((column, idx2) => ( + + ))} +
+ ))} +
+) + +export default CutomsersSliderDesktop diff --git a/apps/www/components/CustomerStories/index.tsx b/apps/www/components/CustomerStories/index.tsx index 37544cbf784..855f420d838 100644 --- a/apps/www/components/CustomerStories/index.tsx +++ b/apps/www/components/CustomerStories/index.tsx @@ -1,11 +1,11 @@ import { PropsWithChildren } from 'react' +import dynamic from 'next/dynamic' import Link from 'next/link' import Image from 'next/image' import { useTheme } from 'next-themes' -import { range } from 'lodash' import { ArrowRight } from 'lucide-react' -import { Button, cn } from 'ui' +import { Button, cn } from 'ui' import SectionContainer from '~/components/Layouts/SectionContainer' import SectionHeader from 'components/UI/SectionHeader' import Panel from '~/components/Panel' @@ -13,150 +13,124 @@ import Panel from '~/components/Panel' import customerStories from '~/data/CustomerStories' import type { CustomerStoryType } from '~/data/CustomerStories' -const getCustomer = (customer: string, linked: boolean) => ({ - ...customerStories.find((story: any) => customer.includes(story.organization)), +const CustomersSliderMobile = dynamic(() => import('./CustomersSliderMobile')) +const CutomsersSliderDesktop = dynamic(() => import('./CutomsersSliderDesktop')) + +const CustomerStories = () => ( +
+ + +
+ + +
+
+ + +
+) + +/** + * Utils & Components + */ + +const getCustomer: (customer: string, linked: boolean) => ExtendedCustomerStoryType = ( + customer, + linked +) => ({ + ...customerStories.find((story: CustomerStoryType) => customer.includes(story.organization!))!, linked, }) -const CustomerStories = () => { - const composition = [ - { - type: 'narrow', - cards: [getCustomer('Quivr', false), getCustomer('Tinloof', false)], - }, - { - type: 'narrow', - cards: [ - { - logo: '/images/customers/logos/1password.png', - logo_inverse: '/images/customers/logos/light/1password.png', - organization: '1Password', - }, - getCustomer('Next Door Lending', true), - ], - }, - { - type: 'expanded', - cards: [getCustomer('Maergo', true)], - }, - { - type: 'narrow', - cards: [ - getCustomer('Shotgun', true), - { - logo: '/images/customers/logos/mozilla.png', - logo_inverse: '/images/customers/logos/light/mozilla.png', - organization: 'Mozilla', - linked: false, - }, - ], - }, - { - type: 'expanded', - cards: [getCustomer('Chatbase', true)], - }, - { - type: 'narrow', - cards: [getCustomer('Mobbin', true), getCustomer('HappyTeams', true)], - }, - { - type: 'expanded', - cards: [getCustomer('Pebblely', true)], - }, - ] - - const compositionGap = 'gap-4' +const compositionCols: CompositionColType[] = [ + { + type: 'narrow', + cards: [getCustomer('Quivr', false), getCustomer('Tinloof', false)], + }, + { + type: 'narrow', + cards: [ + { + logo: '/images/customers/logos/1password.png', + logo_inverse: '/images/customers/logos/light/1password.png', + organization: '1Password', + }, + getCustomer('Next Door Lending', true), + ], + }, + { + type: 'expanded', + cards: [getCustomer('Maergo', true)], + }, + { + type: 'narrow', + cards: [ + getCustomer('Shotgun', true), + { + logo: '/images/customers/logos/mozilla.png', + logo_inverse: '/images/customers/logos/light/mozilla.png', + organization: 'Mozilla', + linked: false, + }, + ], + }, + { + type: 'expanded', + cards: [getCustomer('Chatbase', true)], + }, + { + type: 'narrow', + cards: [getCustomer('Mobbin', true), getCustomer('HappyTeams', true)], + }, + { + type: 'expanded', + cards: [getCustomer('Pebblely', true)], + }, +] +export const CompositionCol: React.FC = ({ column, className }) => { return ( -
- - -
- - -
-
-
- {range(0, 2).map((_, idx1: number) => ( -
+ {column.cards.map((customer) => + column.type === 'expanded' ? ( + - {composition.map((group, idx2) => ( -
- {group.cards.map((customer: any, idx3) => - group.type === 'expanded' ? ( - - - - ) : customer.linked ? ( - - - - ) : ( - - ) - )} -
- ))} -
- ))} -
+ + + ) : customer.linked ? ( + + + + ) : ( + + ) + )}
) } -interface CustomerCardProps extends PropsWithChildren { - customer: CustomerStoryType - className?: string - size?: 'narrow' | 'expanded' -} - const CustomerCard: React.FC = ({ customer, className, @@ -173,7 +147,7 @@ const CustomerCard: React.FC = ({
{customer.title} = ({ return ( {customer.organization} = ({ hasActiveOnHover outerClassName={cn( 'relative', - 'h-full', + 'w-full h-full', 'group', 'cursor-pointer', 'overflow-hidden', @@ -216,13 +190,13 @@ const CustomerCard: React.FC = ({ 'transition', className )} - innerClassName="h-full p-8 flex flex-col gap-6 justify-between" + innerClassName="h-full p-4 md:p-8 flex flex-col gap-6 justify-between" {...rest} > {showLogoInverse && } - {showLogo && } + {showLogo && }

{customer.title}

{children && {children}} @@ -231,4 +205,25 @@ const CustomerCard: React.FC = ({ } } +/** Types */ + +type ColSize = 'narrow' | 'expanded' +type ExtendedCustomerStoryType = Partial & { linked?: boolean } + +export type CompositionColType = { + type: ColSize + cards: ExtendedCustomerStoryType[] +} + +interface CompositionColProps { + column: CompositionColType + className?: string +} + +interface CustomerCardProps extends PropsWithChildren { + customer: ExtendedCustomerStoryType + className?: string + size?: ColSize +} + export default CustomerStories diff --git a/apps/www/components/DashboardFeatures/index.tsx b/apps/www/components/DashboardFeatures/index.tsx index 044ed15e940..986747f8216 100644 --- a/apps/www/components/DashboardFeatures/index.tsx +++ b/apps/www/components/DashboardFeatures/index.tsx @@ -10,7 +10,9 @@ interface Props { const DashboardFeatures: React.FC = ({ title, tabs }) => ( -

{title}

+

+ {title} +

) diff --git a/apps/www/components/ExampleCard.tsx b/apps/www/components/ExampleCard.tsx index b93bb4e0af5..f4e38b936dd 100644 --- a/apps/www/components/ExampleCard.tsx +++ b/apps/www/components/ExampleCard.tsx @@ -1,7 +1,8 @@ import { useEffect, useState } from 'react' -import { useBreakpoint } from 'common' import Link from 'next/link' import { ArrowUpRight } from 'lucide-react' +import { useBreakpoint } from 'common' + import { Button } from 'ui' import Panel from './Panel' @@ -17,7 +18,7 @@ function ExampleCard(props: any) { return ( - +
{props.tags && (
diff --git a/apps/www/components/Products/AuthVisual.tsx b/apps/www/components/Products/AuthVisual.tsx index 890a955e830..3a92ac5563d 100644 --- a/apps/www/components/Products/AuthVisual.tsx +++ b/apps/www/components/Products/AuthVisual.tsx @@ -1,10 +1,15 @@ import Image from 'next/image' import React from 'react' +import { cn } from 'ui' -const AuthVisual = () => { +interface Props { + className?: string +} + +const AuthVisual: React.FC = ({ className }) => { return (
diff --git a/apps/www/components/Products/DataAPIsVisual.tsx b/apps/www/components/Products/DataAPIsVisual.tsx index 6c36f88457f..1a786de95ac 100644 --- a/apps/www/components/Products/DataAPIsVisual.tsx +++ b/apps/www/components/Products/DataAPIsVisual.tsx @@ -1,9 +1,14 @@ import React from 'react' import Image from 'next/image' import { range } from 'lodash' +import { cn } from 'ui' -const DataAPIsVisual = () => ( -
+interface Props { + className?: string +} + +const DataAPIsVisual: React.FC = ({ className }) => ( +
{range(0, 3).map((_, idx1: number) => (
{ +interface Props { + className?: string +} + +const DatabaseVisual: React.FC = ({ className }) => { const containerRef = useRef(null) const ref = useRef(null) const [gradientPos, setGradientPos] = useState({ x: 0, y: 0 }) @@ -49,7 +54,7 @@ const DatabaseVisual = () => { return (
{ +interface Props { + className?: string +} + +const FunctionsVisual: React.FC = ({ className }) => { const typerRef = useRef(null) const isMobile = useBreakpoint('md') const [typed, setTyped] = useState(null) @@ -64,7 +69,7 @@ const FunctionsVisual = () => { return (
diff --git a/apps/www/components/Products/RealtimeVisual.tsx b/apps/www/components/Products/RealtimeVisual.tsx index 5f0d5c3299f..895ff41db61 100644 --- a/apps/www/components/Products/RealtimeVisual.tsx +++ b/apps/www/components/Products/RealtimeVisual.tsx @@ -1,8 +1,12 @@ import React, { useRef, useState } from 'react' import Image from 'next/image' import { isBrowser, useReducedMotion } from 'common' +import { cn } from 'ui' +interface Props { + className?: string +} -const RealtimeVisual = () => { +const RealtimeVisual: React.FC = ({ className }) => { const cardRef = useRef(null) const [svgTransformSelf, setSvgTransformSelf] = useState('translate(0px, 0px)') const [svgTransform, setSvgTransform] = useState('translate(0px, 0px)') @@ -39,7 +43,10 @@ const RealtimeVisual = () => { return (
{ +interface Props { + className?: string +} + +const StorageVisual: React.FC = ({ className }) => { const cols = [ , , @@ -10,7 +15,7 @@ const StorageVisual = () => { return (
diff --git a/apps/www/components/Products/VectorVisual.tsx b/apps/www/components/Products/VectorVisual.tsx index 9db7aae8392..fe3fa482c95 100644 --- a/apps/www/components/Products/VectorVisual.tsx +++ b/apps/www/components/Products/VectorVisual.tsx @@ -1,8 +1,13 @@ import { detectBrowser, isBrowser } from 'common' import Image from 'next/image' import React, { useEffect, useRef, useState } from 'react' +import { cn } from 'ui' -const VectorVisual = () => { +interface Props { + className?: string +} + +const VectorVisual: React.FC = ({ className }) => { const containerRef = useRef(null) const ref = useRef(null) const [gradientPos, setGradientPos] = useState({ x: 0, y: 0 }) @@ -49,15 +54,16 @@ const VectorVisual = () => { return (
{ +const Products: React.FC = (props) => { const router = useRouter() const telemetryProps = useTelemetryProps() @@ -97,7 +97,7 @@ const Products = (props: Props) => { } onClick={() => sendTelemetryEvent(PRODUCT_SHORTNAMES.DATABASE)} - image={} + image={} /> { subtitle={ <> Add user sign ups and logins, -
- securing your data with Row Level Security. +
securing your data with Row Level Security. } - image={} + image={} onClick={() => sendTelemetryEvent(PRODUCT_SHORTNAMES.AUTHENTICATION)} /> { subtitle={ <> Easily write custom code -
{' '} +
{' '} without deploying or scaling servers. } onClick={() => sendTelemetryEvent(PRODUCT_SHORTNAMES.FUNCTIONS)} - image={} + image={} /> { subtitle={ <> Store, organize, and serve -
large files, from videos to - images. +
large files, from + videos to images. } - image={} + image={} onClick={() => sendTelemetryEvent(PRODUCT_SHORTNAMES.STORAGE)} /> { subtitle={ <> Build multiplayer experiences -
with real-time data - synchronization. +
with real-time data synchronization. } onClick={() => sendTelemetryEvent(PRODUCT_SHORTNAMES.REALTIME)} - image={} + image={} className=" col-span-6 pointer-events-none xl:col-span-3 hover:!cursor-[url('/images/index/products/realtime-cursor-light.svg'),_auto] @@ -175,7 +173,7 @@ const Products = (props: Props) => { title={props.products['vector'].name} subtitle={ <> - Integrate your favorite ML-models to
+ Integrate your favorite ML-models to
store, index and search vector embeddings. } @@ -243,7 +241,7 @@ const Products = (props: Props) => { } onClick={() => sendTelemetryEvent(PRODUCT_SHORTNAMES.VECTOR)} - image={} + image={} /> { title={props.products['data-api'].name} subtitle={props.products['data-api'].description} onClick={() => sendTelemetryEvent('data-api')} - image={} + image={} />

Use one or all. Best of breed products. Integrated diff --git a/apps/www/components/TwitterSocialSection.tsx b/apps/www/components/TwitterSocialSection.tsx index e6a2f9bd19c..bf903fd146f 100644 --- a/apps/www/components/TwitterSocialSection.tsx +++ b/apps/www/components/TwitterSocialSection.tsx @@ -12,10 +12,12 @@ const TwitterSocialSection = () => { return ( <> - +

Join the community

-

Discover what our community has to say about their Supabase experience.

-
+

+ Discover what our community has to say about their Supabase experience. +

+
- + diff --git a/apps/www/data/Examples.ts b/apps/www/data/Examples.ts index a12986071c1..b6bfcb61b62 100644 --- a/apps/www/data/Examples.ts +++ b/apps/www/data/Examples.ts @@ -1,6 +1,21 @@ import { PRODUCT_NAMES } from 'shared-data/products' -export default [ +export type Example = { + type: string + tags: string[] + products: PRODUCT_NAMES[] + title: string + description: string + author?: string + author_url?: string + author_img?: string + repo_name?: string + repo_url?: string + vercel_deploy_url?: string + demo_url?: string +} + +const Examples: Example[] = [ { type: 'example', tags: ['Next.js', 'Stripe', 'Vercel'], @@ -287,3 +302,5 @@ export default [ 'https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/file-upload-storage', }, ] + +export default Examples diff --git a/apps/www/data/home/content.tsx b/apps/www/data/home/content.tsx index e511930f612..aea0dca0929 100644 --- a/apps/www/data/home/content.tsx +++ b/apps/www/data/home/content.tsx @@ -51,8 +51,7 @@ export default { title: ( <> Stay productive and manage your app -
- without leaving the dashboard +
without leaving the dashboard ), tabs: [ diff --git a/apps/www/pages/contact/sales.tsx b/apps/www/pages/contact/sales.tsx index 15462bce40f..2d5c0f081a2 100644 --- a/apps/www/pages/contact/sales.tsx +++ b/apps/www/pages/contact/sales.tsx @@ -137,7 +137,7 @@ export const CustomerQuote = ({ ) export const CustomerLogo = ({ title, logo }: { title: string; logo: string }) => ( -
+
-
+

Customer stories

-

+

Discover case studies on how Supabase is being used around the world to quickly create outstanding products and set new industry standards.

@@ -125,7 +125,7 @@ function CustomerStoriesPage(props: any) {
diff --git a/apps/www/pages/customers/[slug].tsx b/apps/www/pages/customers/[slug].tsx index ca0bd98f663..b14f42421c7 100644 --- a/apps/www/pages/customers/[slug].tsx +++ b/apps/www/pages/customers/[slug].tsx @@ -115,12 +115,12 @@ function CaseStudyPage(props: any) {
-
+
{/* Back button */}
-
+
-
- +
+ Customer Stories

{title}

diff --git a/apps/www/public/sitemap_www.xml b/apps/www/public/sitemap_www.xml index 4edc7b3b653..d4f56279ae8 100644 --- a/apps/www/public/sitemap_www.xml +++ b/apps/www/public/sitemap_www.xml @@ -66,6 +66,12 @@ 0.5 + + https://supabase.com/enterprise + weekly + 0.5 + + https://supabase.com/ga-week weekly @@ -229,13 +235,13 @@ - https://supabase.com/partners/experts + https://supabase.com/partners/integrations weekly 0.5 - https://supabase.com/partners/integrations + https://supabase.com/partners/experts weekly 0.5 @@ -2024,6 +2030,12 @@ 0.5 + + https://supabase.com/blog/lw12-hackathon-winners + weekly + 0.5 + + https://supabase.com/customers/berriai weekly