diff --git a/apps/www/_events/scale-to-billions-generative-ai-humata.mdx b/apps/www/_events/2024-08-21-scale-to-billions-generative-ai-humata.mdx similarity index 96% rename from apps/www/_events/scale-to-billions-generative-ai-humata.mdx rename to apps/www/_events/2024-08-21-scale-to-billions-generative-ai-humata.mdx index ee729a1fdce..d88770b4510 100644 --- a/apps/www/_events/scale-to-billions-generative-ai-humata.mdx +++ b/apps/www/_events/2024-08-21-scale-to-billions-generative-ai-humata.mdx @@ -4,7 +4,7 @@ meta_title: 'Scale to Billions: Generative AI/Humata | 21 Aug 2024 | Webinar' subtitle: 'Quickly and cost effectively build Generative AI applications that you can use to chat with your company’s documents.' meta_description: 'Quickly and cost effectively build Generative AI applications that you can use to chat with your company’s documents.' type: 'webinar' -image: 'webinars/scale-to-billions-generative-ai-humata/og.png' +onDemand: true date: '2024-08-21T10:00:00.000-07:00' timezone: 'America/Los_Angeles' duration: '45 mins' @@ -21,7 +21,7 @@ main_cta: { url: 'https://zoom.us/webinar/register/WN_cTcp6uv4S2OI63jDrOb1Ng', target: '_blank', - label: 'Register now', + label: 'Watch now', } speakers: 'gregnr,khajvandi' --- diff --git a/apps/www/_events/2024-08-30__launch-week-12-meetup-tenerife-spain.mdx b/apps/www/_events/2024-08-30__launch-week-12-meetup-tenerife-spain.mdx new file mode 100644 index 00000000000..fa36c445b72 --- /dev/null +++ b/apps/www/_events/2024-08-30__launch-week-12-meetup-tenerife-spain.mdx @@ -0,0 +1,13 @@ +--- +title: 'Supabase Launch Week 12 Meetup: Tenerife, España' +subtitle: 'Join the community @ Tándem & Coworking - La Orotava, Canarias' +type: 'meetup' +onDemand: false +disable_page_build: true +link: { href: https://lu.ma/5219ibrl, target: '_blank' } +date: '2024-08-30T10:00:00.000+01:00' +timezone: 'Atlantic/Canary' +duration: '2 hours' +categories: + - meetup +--- diff --git a/apps/www/_events/2024-09-05__hack-night-v0-19.mdx b/apps/www/_events/2024-09-05__hack-night-v0-19.mdx new file mode 100644 index 00000000000..820c51291f5 --- /dev/null +++ b/apps/www/_events/2024-09-05__hack-night-v0-19.mdx @@ -0,0 +1,13 @@ +--- +title: 'hack night v0.19' +subtitle: '​where miami builds cool shit.' +type: 'talk' +onDemand: false +disable_page_build: true +link: { href: https://lu.ma/x62id5w8, target: '_blank' } +date: '2024-09-05T18:00:00.000-04:00' +timezone: 'America/New_York' +duration: '5 hours' +categories: + - talk +--- diff --git a/apps/www/_events/2024-09-05__launch-week-12-meetup-tenerife-bucharest-romania copy.mdx b/apps/www/_events/2024-09-05__launch-week-12-meetup-tenerife-bucharest-romania copy.mdx new file mode 100644 index 00000000000..55e633bcf5b --- /dev/null +++ b/apps/www/_events/2024-09-05__launch-week-12-meetup-tenerife-bucharest-romania copy.mdx @@ -0,0 +1,13 @@ +--- +title: 'Supabase Launch Week 12 Meetup: Bucharest, Romania' +subtitle: 'Join the community in Romania' +type: 'meetup' +onDemand: false +disable_page_build: true +link: { href: https://lu.ma/x910h8kb, target: '_blank' } +date: '2024-09-05T18:00:00.000+01:00' +timezone: 'Europe/Belgrade' +duration: '2 hours' +categories: + - meetup +--- diff --git a/apps/www/_events/2024-09-07__launch-week-12-meetup-tenerife-Ibadan-nigeria.mdx b/apps/www/_events/2024-09-07__launch-week-12-meetup-tenerife-Ibadan-nigeria.mdx new file mode 100644 index 00000000000..218afe3fbfa --- /dev/null +++ b/apps/www/_events/2024-09-07__launch-week-12-meetup-tenerife-Ibadan-nigeria.mdx @@ -0,0 +1,13 @@ +--- +title: 'Supabase Launch Week 12 Meetup: Ibadan, Nigeria' +subtitle: 'Join the community in Nigeria' +type: 'meetup' +onDemand: false +disable_page_build: true +link: { href: https://lu.ma/x910h8kb, target: '_blank' } +date: '2024-09-07T09:00:00.000+01:00' +timezone: 'Africa/Algiers' +duration: '3 hours' +categories: + - meetup +--- diff --git a/apps/www/_events/2024-09-12-launch-week-12-hackathon.mdx b/apps/www/_events/2024-09-12-launch-week-12-hackathon.mdx new file mode 100644 index 00000000000..38de79e4dd0 --- /dev/null +++ b/apps/www/_events/2024-09-12-launch-week-12-hackathon.mdx @@ -0,0 +1,108 @@ +--- +title: 'Supabase Launch Week 12 Hackathon' +meta_title: 'Build an Open Source Project over 10 days. 5 prize categories.' +subtitle: 'Build an Open Source Project over 10 days. 5 prize categories.' +meta_description: 'Build an Open Source Project over 10 days. 5 prize categories.' +type: 'hackathon' +onDemand: false +date: '2024-09-13T10:00:00.000-07:00' +timezone: 'America/Los_Angeles' +duration: '10 days' +categories: + - hackathon +main_cta: + { + url: 'https://hackathon.dev/lw12', + target: '_blank', + label: 'Submit project', + disabled_label: 'Submissions closed', + } +--- + +The hackathon starts on Friday, September 13th at 09:00 am PT and ends on Sunday, September 22nd at 11:59 pm PT. You could win an extremely limited edition Supabase swag and add your name to the Supabase Hackathon Hall of Fame. + +For some inspiration, check out all the [winners from previous hackathons](https://supabase.com/blog/tags/hackathon). + +This is the perfect excuse to "Build in a weekend, scale to billions.” Since you retain all the rights to your submissions, you can use the hackathon as a launch pad for your new Startup ideas, side projects, or indie hacks. + +## Key Facts + +- You have 10 days to build a new **Open Source** project using Supabase in some capacity + - Starting 09:00 am PT Friday, September 13th 2024 + - The submission deadline is 11:59 pm Sunday midnight PT, September 22nd 2024 +- Enter as an individual or as a team of up to 4 people +- Build whatever you want - a project, app, tool, library. Anything. +- 1-minute video containing the following: + - Name of the project + - Demonstration of the project + - How Supabase is used within the project +- [Here is an example video](https://youtu.be/KaWJQzTTx5k). We do not assess the quality of the video itself. Remember to keep it concise. + +## Prizes + +There are 5 categories there will be prizes for: + +- Best overall project (Swag kit) +- Best use of AI (Swag kit) +- Most fun / best easter egg (Swag kit) +- Most technically impressive (Swag kit) +- Most visually pleasing (Swag kit) + +There will be a winner and a runner-up prize for each category. Every team member on winning/runner-up teams gets a Supabase Launch Week swag kit. + +## Submission + +You should submit your project using [this form](https://hackathon.dev/lw12) before 11:59 pm Sunday midnight PT, September 22nd, 2024. + +## Judges + +The Supabase team will judge the winners for each category. +We will be looking for: + +- creativity/inventiveness +- functions correctly/smoothly +- visually pleasing +- technically impressive +- use of Supabase features +- FUN! 😃 + +## Rules + +- Team size 1-4 (all team members on winning teams will receive a prize) +- You cannot be on multiple teams +- One submission per team +- It's not a requirement to use AI +- All design elements, code, etc., for your project must be created **during** the event +- All entries must be Open Source (link to source code required in entry) +- Must use Supabase in some capacity +- Can be any language or framework +- You must submit before the deadline (no late entries) +- Include a link to a 1-minute demo video + +## Resources + +### Supabase resources to get you started + +- [Real world RAG, the missing pieces for your AI application](https://www.youtube.com/watch?v=ibzlEQmgPPY) +- [Simplify complex SQL queries with Views](https://www.youtube.com/watch?v=IOYFS-2lFjU&t) +- [Row Level Security in Supabase](https://supabase.com/docs/guides/database/postgres/row-level-security) + +### Community help + +The Supabase Team will be taking part in the Hackathon and you'll find us live to build in our discord all week. Please join us by building in public: + +- Text channel: [#hackathon](https://discord.gg/UYyweApy) +- Audio channel: [#hackathon](https://discord.gg/Vj3mTPwH) + +If you need help or advice when building, find other people to join your team, or if you just want to chill and watch people build, come and join us! + +[Join our Discord](https://discord.supabase.com/) + +- [Previous Hackathon Prize Winners](https://supabase.com/blog/tags/hackathon) + +![Discord](/images/blog/lw12/hackathon/discord.png) + +## Additional Info + +- Any intellectual property developed during the hackathon will belong to the team that developed it. We expect that each team will have an agreement between themselves regarding the IP, but this is not required. +- By making a submission, you grant Supabase permission to use screenshots, code snippets, and/or links to your project or content of your README on our Twitter, blog, website, email updates, and in the Supabase discord server. Supabase does not make any claims over your IP. diff --git a/apps/www/components/Blog/BlogListItem.tsx b/apps/www/components/Blog/BlogListItem.tsx index 21ec2b6747b..d6a2974211e 100644 --- a/apps/www/components/Blog/BlogListItem.tsx +++ b/apps/www/components/Blog/BlogListItem.tsx @@ -50,13 +50,13 @@ const BlogListItem = ({ post }: Props) => { })} {post.categories && ( -
+
{post.categories.map( (category, i) => i === 0 && ( {category} @@ -65,7 +65,7 @@ const BlogListItem = ({ post }: Props) => {
)} {post.date && ( -

+

{dayjs(post.date).format('D MMM YYYY')}

)} diff --git a/apps/www/components/Events/EventGridItem.tsx b/apps/www/components/Events/EventGridItem.tsx new file mode 100644 index 00000000000..319d916602d --- /dev/null +++ b/apps/www/components/Events/EventGridItem.tsx @@ -0,0 +1,69 @@ +import React from 'react' +// import authors from 'lib/authors.json' +import Image from 'next/image' +import Link from 'next/link' +// import type Author from '~/types/author' +import type PostTypes from '../../types/post' + +import dayjs from 'dayjs' + +interface Props { + event: PostTypes +} + +const EventGridItem = ({ event }: Props) => { + // const authorArray: string[] | undefined = event.author ? event.author.split(',') : [] + // const author = [] + + // if (authorArray) { + // for (let i = 0; i < authorArray.length; i++) { + // author.push( + // authors.find((authors: Author) => { + // return authors.author_id === authorArray[i] + // }) + // ) + // } + // } + + return ( + +
+
+
+ {!event.thumb ? ( +
+ ) : ( + {`${event.title} + )} +
+ + {event.date && ( +
+

{dayjs(event.date).format('D MMM YYYY')}

+ {event.readingTime && ( + <> +

•

+

{event.readingTime}

+ + )} +
+ )} +

{event.title}

+

{event.description}

+
+
+ + ) +} + +export default EventGridItem diff --git a/apps/www/components/Events/EventListItem.tsx b/apps/www/components/Events/EventListItem.tsx new file mode 100644 index 00000000000..09ec64d2729 --- /dev/null +++ b/apps/www/components/Events/EventListItem.tsx @@ -0,0 +1,82 @@ +import authors from 'lib/authors.json' +import Link from 'next/link' +import React from 'react' +import type Author from '~/types/author' +import type PostTypes from '~/types/post' +import dayjs from 'dayjs' + +import utc from 'dayjs/plugin/utc' +import timezone from 'dayjs/plugin/timezone' +import advancedFormat from 'dayjs/plugin/advancedFormat' +import { Badge } from 'ui' + +dayjs.extend(utc) +dayjs.extend(timezone) +dayjs.extend(advancedFormat) + +interface Props { + event: PostTypes +} + +const EventListItem = ({ event }: Props) => { + const authorArray: string[] | undefined = event?.author ? event.author.split(',') : [] + const author = [] + + if (authorArray) { + for (let i = 0; i < authorArray.length; i++) { + author.push( + authors.find((authors: Author) => { + return authors.author_id === authorArray[i] + }) + ) + } + } + + return ( + +
+

{event.title}

+
+
+ {event.categories && ( +
+ {/* {event.onDemand && ( + On Demand + )} */} + {event.categories.map( + (category, i) => + i === 0 && ( + + {category} + + ) + )} +
+ )} + {event.date && ( +

+ {dayjs(event.date) + .tz(event.timezone ?? 'America/Los_Angeles') + .format('DD MMM YYYY')} + + + {dayjs(event.date) + .tz(event.timezone ?? 'America/Los_Angeles') + .format('hA')} + + + {dayjs(event.date) + .tz(event.timezone ?? 'America/Los_Angeles') + .format('z')} +

+ )} +
+ + ) +} + +export default EventListItem diff --git a/apps/www/components/Events/EventsFilters.tsx b/apps/www/components/Events/EventsFilters.tsx new file mode 100644 index 00000000000..00df9bd0bdd --- /dev/null +++ b/apps/www/components/Events/EventsFilters.tsx @@ -0,0 +1,202 @@ +import { useEffect, useState } from 'react' +import { useRouter } from 'next/router' +import { useSearchParams } from 'next/navigation' +import { AnimatePresence, motion } from 'framer-motion' +import { startCase } from 'lodash' +import { useKey } from 'react-use' +import { useBreakpoint } from 'common' +import type PostTypes from '~/types/post' + +import { + Button, + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, + IconChevronDown, + cn, +} from 'ui' + +interface Props { + allEvents: PostTypes[] + events?: PostTypes[] + setEvents: (posts: any) => void + categories: { [key: string]: number } +} + +/** + * search via category if no q param + * search via category and reset q param if present + */ + +function EventFilters({ allEvents, setEvents, categories }: Props) { + const [category, setCategory] = useState('all') + const [searchTerm, setSearchTerm] = useState('') + const [showSearchInput, setShowSearchInput] = useState(false) + + const router = useRouter() + const searchParams = useSearchParams() + const q = searchParams?.get('q') + const activeCategory = searchParams?.get('category') + const isMobile = useBreakpoint(1023) + const is2XL = useBreakpoint(1535) + + useEffect(() => { + if (!q) { + handlePosts() + } + }, [category]) + + useEffect(() => { + if (q) { + handleSearchByText(q) + } + }, [q]) + + const handleReplaceRouter = () => { + if (!searchTerm && category !== 'all') { + router.query.category = category + router.replace(router, undefined, { shallow: true, scroll: false }) + } + } + + const handlePosts = () => { + handleReplaceRouter() + + setEvents( + category === 'all' + ? allEvents + : allEvents.filter((event: any) => { + const found = event.categories?.includes(category) + return found + }) + ) + } + + useKey('Escape', () => handleSearchByText('')) + + useEffect(() => { + setShowSearchInput(!isMobile) + }, [isMobile]) + + useEffect(() => { + if (router.isReady && q) { + setSearchTerm(q) + } + if (router.isReady && activeCategory && activeCategory !== 'all') { + setCategory(activeCategory) + } + }, [activeCategory, router.isReady, q]) + + const handleSearchByText = (text: string) => { + setSearchTerm(text) + searchParams?.has('q') && router.replace('/events', undefined, { shallow: true, scroll: false }) + router.replace(`/events?q=${text}`, undefined, { shallow: true, scroll: false }) + if (text.length < 1) router.replace('/events', undefined, { shallow: true, scroll: false }) + + const matches = allEvents.filter((event: any) => { + const found = + event.tags?.join(' ').replaceAll('-', ' ').includes(text.toLowerCase()) || + event.title?.toLowerCase().includes(text.toLowerCase()) || + event.author?.includes(text.toLowerCase()) + return found + }) + + setEvents(matches) + } + + const handleSetCategory = (category: string) => { + searchTerm && handlePosts() + searchTerm && setSearchTerm('') + setCategory(category) + category === 'all' + ? router.replace('/events', undefined, { shallow: true, scroll: false }) + : router.replace(`/events?category=${category}`, undefined, { + shallow: true, + scroll: false, + }) + } + + return ( +
+ + {!showSearchInput && ( + + + + + + + {Object.entries(categories).map(([category, count]) => ( + handleSetCategory(category)} + className={cn( + 'flex gap-0.5 items-center justify-between', + (category === 'all' && !activeCategory) || category === activeCategory + ? 'text-brand-600' + : '' + )} + > + {category === 'all' ? 'All Posts' : startCase(category.replaceAll('-', ' '))}{' '} + 9 && 'aspect-auto px-1' + )} + > + {count} + + + ))} + + + + )} +
+ {Object.entries(categories).map(([category, count]) => ( + + ))} +
+
+
+ ) +} + +export default EventFilters diff --git a/apps/www/data/Footer.ts b/apps/www/data/Footer.ts index 9f0c0ba791b..8e41357aa98 100644 --- a/apps/www/data/Footer.ts +++ b/apps/www/data/Footer.ts @@ -135,6 +135,10 @@ const footerData = [ text: 'Company', url: '/company', }, + { + text: 'Events', + url: '/events', + }, { text: 'General Availability', url: '/ga', diff --git a/apps/www/internals/generate-sitemap.mjs b/apps/www/internals/generate-sitemap.mjs index 1e6ee0f5119..6fd2fc4333a 100644 --- a/apps/www/internals/generate-sitemap.mjs +++ b/apps/www/internals/generate-sitemap.mjs @@ -87,6 +87,27 @@ async function generate() { route = `/${blogUrl}/` + substring } + /** + * Event based urls + * handle removal of dates in filename + */ + if (route.includes(`/${eventsUrl}/`)) { + // remove finelnames with __ + if (route.includes(`__`)) return null + /** + * remove directory from route + */ + const _route = route.replace(`/${eventsUrl}/`, '') + /** + * remove the date from the file name + */ + const substring = _route.substring(11) + /** + * reconsruct the route + */ + route = `/${eventsUrl}/` + substring + } + return ` ${`https://supabase.com${route}`} diff --git a/apps/www/lib/posts.tsx b/apps/www/lib/posts.tsx index 56d572bf5e9..9a73c6352d2 100644 --- a/apps/www/lib/posts.tsx +++ b/apps/www/lib/posts.tsx @@ -2,6 +2,7 @@ import fs from 'fs' import path from 'path' import matter from 'gray-matter' import { generateReadingTime } from './helpers' +import { BlogPost } from '../.contentlayer/generated' type Directories = '_blog' | '_case-studies' | '_customers' | '_alternatives' | '_events' @@ -34,7 +35,7 @@ export const getSortedPosts = ({ const allPosts = fileNames .map((filename) => { const slug = - directory === '_blog' + directory === '_blog' || directory === '_events' ? filename.replace('.mdx', '').substring(FILENAME_SUBSTRING) : filename.replace('.mdx', '') @@ -118,7 +119,7 @@ export const getAllPostSlugs = (directory: Directories) => { ...dates, slug: filename .replace('.mdx', '') - .substring(directory === '_blog' ? FILENAME_SUBSTRING : 0), + .substring(directory === '_blog' || directory === '_events' ? FILENAME_SUBSTRING : 0), }, } }) diff --git a/apps/www/pages/_app.tsx b/apps/www/pages/_app.tsx index 4cac9922c1e..83d5b6334ca 100644 --- a/apps/www/pages/_app.tsx +++ b/apps/www/pages/_app.tsx @@ -3,7 +3,7 @@ import 'config/code-hike.scss' import '../styles/index.css' import { SessionContextProvider } from '@supabase/auth-helpers-react' -import { AuthProvider, ThemeProvider, useTelemetryProps, useThemeSandbox } from 'common' +import { AuthProvider, IS_PROD, ThemeProvider, useTelemetryProps, useThemeSandbox } from 'common' import { DefaultSeo } from 'next-seo' import { AppProps } from 'next/app' import Head from 'next/head' @@ -18,7 +18,7 @@ import MetaFaviconsPagesRouter, { DEFAULT_FAVICON_THEME_COLOR, } from 'common/MetaFavicons/pages-router' import { WwwCommandMenu } from '~/components/CommandMenu' -import { API_URL, APP_NAME, DEFAULT_META_DESCRIPTION } from '~/lib/constants' +import { API_URL, APP_NAME, DEFAULT_META_DESCRIPTION, IS_PREVIEW } from '~/lib/constants' import { post } from '~/lib/fetchWrapper' import supabase from '~/lib/supabase' import useDarkLaunchWeeks from '../hooks/useDarkLaunchWeeks' @@ -27,6 +27,8 @@ export default function App({ Component, pageProps }: AppProps) { const router = useRouter() const telemetryProps = useTelemetryProps() const { consentValue, hasAcceptedConsent } = useConsent() + const IS_DEV = !IS_PROD && !IS_PREVIEW + const blockEvents = IS_DEV || !hasAcceptedConsent useThemeSandbox() @@ -43,7 +45,7 @@ export default function App({ Component, pageProps }: AppProps) { } useEffect(() => { - if (!hasAcceptedConsent) return + if (blockEvents) return function handleRouteChange(url: string) { handlePageTelemetry(url) @@ -57,7 +59,7 @@ export default function App({ Component, pageProps }: AppProps) { }, [router.events, consentValue]) useEffect(() => { - if (!hasAcceptedConsent) return + if (blockEvents) return /** * Send page telemetry on first page load */ diff --git a/apps/www/pages/events/[slug].tsx b/apps/www/pages/events/[slug].tsx index 6261bd0e5df..705f36baa51 100644 --- a/apps/www/pages/events/[slug].tsx +++ b/apps/www/pages/events/[slug].tsx @@ -7,7 +7,12 @@ import { MDXRemote } from 'next-mdx-remote' import { NextSeo } from 'next-seo' import dayjs from 'dayjs' import matter from 'gray-matter' -import { VideoCameraIcon } from '@heroicons/react/solid' +import { + DesktopComputerIcon, + VideoCameraIcon, + MicrophoneIcon, + HandIcon, +} from '@heroicons/react/solid' import authors from 'lib/authors.json' import { isNotNullOrUndefined } from '~/lib/helpers' @@ -30,16 +35,18 @@ import ShareArticleActions from '~/components/Blog/ShareArticleActions' import * as supabaseLogoWordmarkDark from 'common/assets/images/supabase-logo-wordmark--dark.png' import * as supabaseLogoWordmarkLight from 'common/assets/images/supabase-logo-wordmark--light.png' +import { ChevronLeft } from 'lucide-react' dayjs.extend(utc) dayjs.extend(timezone) dayjs.extend(advancedFormat) -type EventType = 'webinar' | 'launch_week' | 'conference' +type EventType = 'webinar' | 'meetup' | 'conference' | 'talk' | 'hackathon' | 'launch_week' type CTA = { url: string label?: string + disabled_label?: string target?: '_blank' | '_self' } @@ -57,6 +64,8 @@ interface EventData { description: string type: EventType company?: CompanyType + onDemand?: boolean + disable_page_build?: boolean duration?: string timezone?: string tags?: string[] @@ -109,6 +118,12 @@ export const getStaticProps: GetStaticProps = async ({ p const postContent = await getPostdata(filePath, '_events') const { data, content } = matter(postContent) as unknown as MatterReturn + if (data.disable_page_build) { + return { + notFound: true, + } + } + const mdxSource: any = await mdxSerialize(content) return { @@ -132,7 +147,7 @@ const EventPage = ({ event }: InferGetStaticPropsType) => }) .filter(isNotNullOrUndefined) - const IS_REGISTRATION_OPEN = Date.parse(event.date) > Date.now() + const IS_REGISTRATION_OPEN = event.onDemand || Date.parse(event.date) > Date.now() const meta = { title: event.meta_title ?? event.title, @@ -141,9 +156,12 @@ const EventPage = ({ event }: InferGetStaticPropsType) => } const eventIcons = { - webinar: (props: any) => , conference: (props: any) => , + hackathon: (props: any) => , launch_week: (props: any) => , + meetup: (props: any) => , + talk: (props: any) => , + webinar: (props: any) => , } const Icon = eventIcons[event.type] @@ -198,6 +216,18 @@ const EventPage = ({ event }: InferGetStaticPropsType) => }} /> +
+ + + + All Events + + +
+
) => ? event.main_cta?.label ? event.main_cta?.label : 'Register to this event' - : 'Registrations are closed'} + : event.main_cta?.disabled_label + ? event.main_cta?.disabled_label + : 'Registrations are closed'}
@@ -286,49 +318,51 @@ const EventPage = ({ event }: InferGetStaticPropsType) => -
-
- - -
- - - - - -
+ {event.company && ( +
+
+ + +
+ + + + + +
+ )}

@@ -353,7 +387,9 @@ const EventPage = ({ event }: InferGetStaticPropsType) => ? event.main_cta?.label ? event.main_cta?.label : 'Register now' - : 'Registrations are closed'} + : event.main_cta?.disabled_label + ? event.main_cta?.disabled_label + : 'Registrations are closed'} diff --git a/apps/www/pages/events/index.tsx b/apps/www/pages/events/index.tsx new file mode 100644 index 00000000000..15177e1ebc6 --- /dev/null +++ b/apps/www/pages/events/index.tsx @@ -0,0 +1,157 @@ +import { useState } from 'react' +import { useRouter } from 'next/router' +import { NextSeo } from 'next-seo' + +import { getSortedPosts } from '~/lib/posts' + +import { cn } from 'ui' +import DefaultLayout from '~/components/Layouts/Default' +import EventListItem from '~/components/Events/EventListItem' +import EventsFilters from '~/components/Events/EventsFilters' +import SectionContainer from '~/components/Layouts/SectionContainer' + +import type BlogPost from '~/types/post' + +interface Props { + events: BlogPost[] + onDemandEvents: BlogPost[] + categories: { [key: string]: number } +} + +function Events({ events: allEvents, onDemandEvents, categories }: Props) { + const [events, setEvents] = useState(allEvents) + const router = useRouter() + + const meta_title = 'Supabase Events: webinars, talks, hackathons, and meetups' + const meta_description = 'Join Supabase and the open-source community at the upcoming events.' + + return ( + <> + + + +

+ Supabase Events +

+

Join us at the following upcoming events

+
+ + + +
    + {events?.length ? ( + events + ?.sort((a, b) => new Date(a.date!).getTime() - new Date(b.date!).getTime()) + .map((event: BlogPost, idx: number) => ( +
    + +
    + )) + ) : ( +

    + No results found +

    + )} +
+
+ +
+

On Demand

+

Replay selected events on your schedule

+
+
    + {onDemandEvents?.length ? ( + onDemandEvents?.map((event: BlogPost, idx: number) => ( +
    + +
    + )) + ) : ( +

    + No results found +

    + )} +
+
+
+ + ) +} + +export async function getStaticProps() { + const allEvents = getSortedPosts({ + directory: '_events', + runner: '** EVENTS PAGE **', + }) as BlogPost[] + const upcomingEvents = allEvents.filter((event: BlogPost) => new Date(event.date!) >= new Date()) + const onDemandEvents = allEvents.filter( + (event: BlogPost) => new Date(event.date!) < new Date() && event.onDemand === true + ) + + const categories = upcomingEvents.reduce( + (acc: { [key: string]: number }, event: BlogPost) => { + // Increment the 'all' counter + acc.all = (acc.all || 0) + 1 + + // Increment the counter for each category + event.categories?.forEach((category) => { + acc[category] = (acc[category] || 0) + 1 + }) + + return acc + }, + { all: 0 } + ) + + return { + props: { + events: upcomingEvents, + onDemandEvents, + categories, + }, + } +} + +export default Events diff --git a/apps/www/public/sitemap_www.xml b/apps/www/public/sitemap_www.xml index c7a1e90402f..e6eb8ddb087 100644 --- a/apps/www/public/sitemap_www.xml +++ b/apps/www/public/sitemap_www.xml @@ -168,6 +168,12 @@ 0.5 + + https://supabase.com/events + weekly + 0.5 + + https://supabase.com/launch-week weekly @@ -210,6 +216,12 @@ 0.5 + + https://supabase.com/partners/integrations + weekly + 0.5 + + https://supabase.com/launch-week/x weekly @@ -222,12 +234,6 @@ 0.5 - - https://supabase.com/partners/integrations - weekly - 0.5 - - https://supabase.com/blog/supabase-alpha-april-2020 weekly @@ -1982,6 +1988,12 @@ 0.5 + + https://supabase.com/blog/supabase-vercel-partnership + weekly + 0.5 + + https://supabase.com/customers/berriai weekly @@ -2090,6 +2102,12 @@ 0.5 + + https://supabase.com/events/launch-week-12-hackathon + weekly + 0.5 + + https://supabase.com/alternatives/supabase-vs-auth0 weekly diff --git a/apps/www/types/post.ts b/apps/www/types/post.ts index 2812ce139be..e0386a64ecb 100644 --- a/apps/www/types/post.ts +++ b/apps/www/types/post.ts @@ -21,6 +21,15 @@ type PostTypes = { logo?: string logo_inverse?: string hideAuthor?: boolean + end_date?: string // for events that span multiple days (not implemented yet) + timezone?: string // utc timezone, e.g. 'America/New_York'. Reference: /apps/studio/components/interfaces/Database/Backups/PITR/PITR.constants.ts + onDemand?: boolean // events that are on-demand following a registration process + disable_page_build?: boolean // when true, we don't build the page and require a custom link + link?: { + href: string + target?: '_blank' | '_self' + label?: string + } // used on event previews to link to a custom event page } export default PostTypes