diff --git a/apps/www/components/MagnifiedProducts.tsx b/apps/www/components/MagnifiedProducts.tsx index e137c9b4314..34864fa3a66 100644 --- a/apps/www/components/MagnifiedProducts.tsx +++ b/apps/www/components/MagnifiedProducts.tsx @@ -71,7 +71,7 @@ function Product({ -
+
{product.name}
diff --git a/apps/www/components/Sections/CustomerQuotesSection.tsx b/apps/www/components/Sections/CustomerQuotesSection.tsx index 7fde1955833..1a9c4f4719e 100644 --- a/apps/www/components/Sections/CustomerQuotesSection.tsx +++ b/apps/www/components/Sections/CustomerQuotesSection.tsx @@ -20,6 +20,7 @@ interface CardInterface { } interface Props { + title: string | React.ReactNode customers: CardInterface[] secondaryLinks: CardInterface[] } @@ -43,10 +44,10 @@ const CustomerQuotesSection = (props: Props) => {
-

{card.author}

-

{card.role}

+

{card.quote}

+

{card.author}

+
{card.role}
-

{card.quote}

{card.url && ( @@ -71,12 +72,9 @@ const CustomerQuotesSection = (props: Props) => {
-

- Join a growing
- community of users -

+

{props.title}

-
+
{props.customers.map((customer) => ( @@ -84,7 +82,7 @@ const CustomerQuotesSection = (props: Props) => { ))} -
+ {/*
{props.secondaryLinks.map((link) => ( @@ -100,7 +98,7 @@ const CustomerQuotesSection = (props: Props) => { ))} -
+
*/}
diff --git a/apps/www/components/Sections/FeaturesSection.tsx b/apps/www/components/Sections/FeaturesSection.tsx index 7553e2b1b4c..457ebf7845d 100644 --- a/apps/www/components/Sections/FeaturesSection.tsx +++ b/apps/www/components/Sections/FeaturesSection.tsx @@ -19,7 +19,7 @@ interface Props { const FeaturesSection = ({ title, paragraph, features }: Props) => { const ref = useRef(null) - const isInView = useInView(ref, { margin: '20%', once: true }) + const isInView = useInView(ref, { margin: '-25%', once: true }) return ( diff --git a/apps/www/components/Sections/HighlightCards.tsx b/apps/www/components/Sections/HighlightCards.tsx index c6b8e8af27c..7cc38cda347 100644 --- a/apps/www/components/Sections/HighlightCards.tsx +++ b/apps/www/components/Sections/HighlightCards.tsx @@ -1,11 +1,13 @@ -import React, { useRef } from 'react' +import React, { useRef, useState } from 'react' import { LazyMotion, domAnimation, m, useInView } from 'framer-motion' import SectionContainer from '~/components/Layouts/SectionContainer' import InteractiveShimmerCard from '~/components/InteractiveShimmerCard' import { INITIAL_BOTTOM, getAnimation } from '~/lib/animations' +import Image from 'next/image' interface Highlight { - image: string + image?: React.ReactNode + svg?: React.ReactNode title: string paragraph: string | React.ReactNode } @@ -26,15 +28,27 @@ const HighlightCards = ({ highlights }: { highlights: Highlight[] }) => { const HighlightCard = ({ highlight, index }: { highlight: Highlight; index: number }) => { const ref = useRef(null) + const [isHovered, setIsHovered] = useState(false) const isInView = useInView(ref, { once: true }) const initial = INITIAL_BOTTOM const animate = getAnimation({ delay: 0.4 + index * 0.1 }) + const Img: any = highlight.image + return ( - + setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} + animate={isInView ? animate : initial} + > -
+
+ {highlight.image && } + {highlight.svg && highlight.svg} +

{highlight.title}

{highlight.paragraph}

diff --git a/apps/www/components/Sections/UseCasesSection.tsx b/apps/www/components/Sections/UseCasesSection.tsx index b4aedd8fbd7..045ad26c4b4 100644 --- a/apps/www/components/Sections/UseCasesSection.tsx +++ b/apps/www/components/Sections/UseCasesSection.tsx @@ -26,7 +26,7 @@ interface Props { const UseCasesSection = ({ title, paragraph, useCases }: Props) => { const ref = useRef(null) - const isInView = useInView(ref, { margin: '20%', once: true }) + const isInView = useInView(ref, { margin: '-25%', once: true }) return ( @@ -65,7 +65,7 @@ const UseCase = ({
-
+
-

{useCase.title}

+

{useCase.title}

{useCase.description}

diff --git a/apps/www/components/products/vector/DeployGlobally.tsx b/apps/www/components/products/vector/DeployGlobally.tsx new file mode 100644 index 00000000000..78e41c7fb88 --- /dev/null +++ b/apps/www/components/products/vector/DeployGlobally.tsx @@ -0,0 +1,215 @@ +import React from 'react' + +function getRandomNumber(min: number, max: number) { + // Calculating the range + var range = max - min + + // Generating a random number within the range + var randomNumber = Math.random() * range + min + + // Rounding the random number to the nearest integer + return Math.round(randomNumber) +} + +const DeployGlobally = ({ isHovered }: { isHovered: boolean }) => { + const ref = React.useRef() + const states = ['off', 'medium', 'high'] + const indices = [ + 3, 11, 14, 23, 27, 35, 42, 48, 55, 62, 67, 71, 81, 83, 91, 98, 103, 107, 110, 115, + ] + const transitionDuration = 250 + + React.useEffect(() => { + if (!isHovered) return + const timeoutIds: any = [] + const circles = [...ref.current?.querySelectorAll('circle')] + + const interval = setInterval(() => { + circles?.map((circle, index: any) => { + const isMatch = indices.includes(index) + + if (!isMatch) { + return + } + + const nextState = states[Math.floor(Math.random() * states.length)] + const currentState = circle.dataset.state + + const pulse = + Math.random() > 0.2 && + ((currentState === 'off' && nextState === 'high') || + (currentState === 'off' && nextState === 'medium') || + (currentState === 'medium' && nextState === 'high')) + + if (pulse) { + const delay = getRandomNumber(50, 150) + + timeoutIds.push( + setTimeout(() => { + circle.style.opacity = '0.3' + }, delay) + ) + + timeoutIds.push( + setTimeout(() => { + circle.style.opacity = '1' + }, transitionDuration + delay) + ) + } + + if (currentState === 'high' && nextState === 'medium' && pulse) { + circle.dataset.state = 'off' + } else { + circle.dataset.state = nextState + } + }) + }, 1000) + + return () => { + clearInterval(interval) + timeoutIds.forEach(clearTimeout) + } + }, [isHovered]) + + return ( + <> +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) +} + +export default DeployGlobally diff --git a/apps/www/components/products/vector/OpenAIImage.tsx b/apps/www/components/products/vector/OpenAIImage.tsx new file mode 100644 index 00000000000..635f12d5dd9 --- /dev/null +++ b/apps/www/components/products/vector/OpenAIImage.tsx @@ -0,0 +1,25 @@ +import Image from 'next/image' +import React from 'react' +import OpenAISVG from './OpenAISVG' + +const OpenAIImage = () => { + return ( + <> +
+ {/* OpenAI vector graphic */} + + + ) +} + +export default OpenAIImage diff --git a/apps/www/components/products/vector/OpenAISVG.tsx b/apps/www/components/products/vector/OpenAISVG.tsx new file mode 100644 index 00000000000..0a2a86843d4 --- /dev/null +++ b/apps/www/components/products/vector/OpenAISVG.tsx @@ -0,0 +1,268 @@ +import React from 'react' + +const OpenAISVG = () => { + return ( +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ ) +} + +export default OpenAISVG diff --git a/apps/www/components/products/vector/PGvectorImg.tsx b/apps/www/components/products/vector/PGvectorImg.tsx new file mode 100644 index 00000000000..85c10fb6745 --- /dev/null +++ b/apps/www/components/products/vector/PGvectorImg.tsx @@ -0,0 +1,343 @@ +import React from 'react' + +const PGvectorImg = () => { + return ( + <> +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) +} + +export default PGvectorImg diff --git a/apps/www/components/products/vector/SecureAndScalableImg.tsx b/apps/www/components/products/vector/SecureAndScalableImg.tsx new file mode 100644 index 00000000000..1af8c820e75 --- /dev/null +++ b/apps/www/components/products/vector/SecureAndScalableImg.tsx @@ -0,0 +1,541 @@ +import React from 'react' + +const SecureAndScalableImg = () => { + return ( + <> +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) +} + +export default SecureAndScalableImg diff --git a/apps/www/data/products/vector/pageData.tsx b/apps/www/data/products/vector/pageData.tsx index 31c195ee0c2..2e91757a7eb 100644 --- a/apps/www/data/products/vector/pageData.tsx +++ b/apps/www/data/products/vector/pageData.tsx @@ -1,5 +1,9 @@ import Link from 'next/link' import IntegrationsSVG from '~/components/products/vector/IntegrationsSVG' +import OpenAIImage from '~/components/products/vector/OpenAIImage' +import SecureAndScalableImg from '~/components/products/vector/SecureAndScalableImg' +import PGvectorImg from '~/components/products/vector/PGvectorImg' +import DeployGlobally from '~/components/products/vector/DeployGlobally' export default { metaTitle: '', @@ -33,13 +37,14 @@ export default { paragraph: // 'Every Supabase project is a dedicated Postgres instance. Use pgvector to store, query, and index your vector embeddings at scale.', 'Use pgvector to store, query, and index your vector embeddings at scale in a Postgres instance.', - image: '', + image: PGvectorImg, }, { title: 'Connect with OpenAI', paragraph: 'Easily connect to any LLM or embeddings API, including Hugging Face, SageMaker and more.', - image: '', + // image: '/images/product/vector/highlight-openai.png', + image: OpenAIImage, }, { title: 'Secure and Scalable', @@ -52,13 +57,13 @@ export default { compliant, and comes with an advanced permissions system. ), - image: '', + image: SecureAndScalableImg, }, { title: 'Deploy Globally', paragraph: 'Choose from 12 Fully Managed datacenters, or Self-Host on your own cloud or servers.', - image: '', + image: DeployGlobally, }, ], }, @@ -270,6 +275,12 @@ docs.query( ], }, quotesSection: { + title: ( + <> + Customers building on
+ Supabase Vector + + ), customers: [ { type: 'customer-story', diff --git a/apps/www/pages/vector/Vector.tsx b/apps/www/pages/vector/Vector.tsx index 8cf1edaeebb..4ff1655c2d8 100644 --- a/apps/www/pages/vector/Vector.tsx +++ b/apps/www/pages/vector/Vector.tsx @@ -1,20 +1,22 @@ import { NextSeo } from 'next-seo' +import dynamic from 'next/dynamic' import { useRouter } from 'next/router' import DefaultLayout from '~/components/Layouts/Default' -import CustomerQuotesSection from '~/components/Sections/CustomerQuotesSection' import ProductHeaderCentered from '~/components/Sections/ProductHeaderCentered' -import FeaturesSection from '~/components/Sections/FeaturesSection' -import HighlightCards from '~/components/Sections/HighlightCards' -import UseCasesSection from '~/components/Sections/UseCasesSection' -import CenteredTitleImage from '~/components/Sections/CenteredTitleImage' -import TimedTabsSection from '~/components/Sections/TimedTabsSection' -import ProductsCta from '~/components/Sections/ProductsCta' -import EnterpriseCta from '~/components/Sections/EnterpriseCta' +import { PRODUCT_SHORTNAMES } from '~/lib/constants' import pageData from '~/data/products/vector/pageData' import 'swiper/swiper.min.css' -import { PRODUCT_NAMES, PRODUCT_SHORTNAMES } from '../../lib/constants' + +const HighlightCards = dynamic(() => import('~/components/Sections/HighlightCards')) +const FeaturesSection = dynamic(() => import('~/components/Sections/FeaturesSection')) +const UseCasesSection = dynamic(() => import('~/components/Sections/UseCasesSection')) +const CenteredTitleImage = dynamic(() => import('~/components/Sections/CenteredTitleImage')) +const CustomerQuotesSection = dynamic(() => import('~/components/Sections/CustomerQuotesSection')) +const TimedTabsSection = dynamic(() => import('~/components/Sections/TimedTabsSection')) +const ProductsCta = dynamic(() => import('~/components/Sections/ProductsCta')) +const EnterpriseCta = dynamic(() => import('~/components/Sections/EnterpriseCta')) function VectorPage() { // base path for images diff --git a/apps/www/public/images/product/vector/highlight-openai.png b/apps/www/public/images/product/vector/highlight-openai.png new file mode 100644 index 00000000000..32cfc362a1c Binary files /dev/null and b/apps/www/public/images/product/vector/highlight-openai.png differ