diff --git a/apps/www/pages/careers.tsx b/apps/www/pages/careers.tsx index f7ad937b331..a9fa84e33d9 100644 --- a/apps/www/pages/careers.tsx +++ b/apps/www/pages/careers.tsx @@ -1,47 +1,59 @@ -import { GetServerSideProps, NextPage } from 'next' +import { GlobeAltIcon } from '@heroicons/react/outline' +import { Check } from 'lucide-react' +import { GetServerSideProps } from 'next' +import { NextSeo } from 'next-seo' import Image from 'next/image' import Link from 'next/link' import { useRouter } from 'next/router' -import { NextSeo } from 'next-seo' -import { GlobeAltIcon } from '@heroicons/react/outline' -import { Check } from 'lucide-react' -import { Badge, Button, Separator, buttonVariants, cn } from 'ui' import ReactMarkdown from 'react-markdown' +import { Badge, Button, buttonVariants, cn, Separator } from 'ui' +import { z } from 'zod' import Styles from '~/styles/career.module.css' import Globe from '~/components/Globe' import DefaultLayout from '~/components/Layouts/Default' import SectionContainer from '~/components/Layouts/SectionContainer' -import { groupJobsByTeam, filterGenericJob, JobItemProps, PLACEHOLDER_JOB_ID } from '~/lib/careers' import career from '~/data/career.json' +import { filterGenericJob, groupJobsByTeam, JobItemProps, PLACEHOLDER_JOB_ID } from '~/lib/careers' + +const ContributorSchema = z.object({ + login: z.string(), + avatar_url: z.string(), + html_url: z.string(), +}) + +type Contributor = z.infer export const getServerSideProps: GetServerSideProps = (async ({ res }) => { // refresh every 5 minutes res.setHeader('Cache-Control', 'public, max-age=300, stale-while-revalidate=300') const job_res = await fetch('https://api.ashbyhq.com/posting-api/job-board/supabase') - const job_data = await job_res.json() + const job_data = (await job_res.json()) as { jobs: JobItemProps[] } - const jobs = groupJobsByTeam(job_data.jobs.filter((job: JobItemProps) => !filterGenericJob(job))) + const jobs = groupJobsByTeam(job_data.jobs.filter((job) => !filterGenericJob(job))) const placeholderJob = job_data.jobs.find(filterGenericJob) - const contributor_res = await fetch( + const contributorResponse = await fetch( 'https://api.github.com/repos/supabase/supabase/contributors?per_page=100' ) - const contributor_arr = await contributor_res.json() + let contributorArray: Contributor[] = [] + try { + const contributorResponseData = await contributorResponse.json() + // if the response is not in the expected format, throw an error and return an empty array + contributorArray = ContributorSchema.array().parse(contributorResponseData) + } catch {} - const contributor_data = await contributor_arr.map( - (contributor: { login: string; avatar_url: string; html_url: string }) => { - return { - login: contributor.login, - avatar_url: contributor.avatar_url, - html_url: contributor.html_url, - } + const contributor_data = contributorArray.map((contributor) => { + return { + login: contributor.login, + avatar_url: contributor.avatar_url, + html_url: contributor.html_url, } - ) + }) - const contributors = await contributor_data.filter((contributor: any) => + const contributors = await contributor_data.filter((contributor) => career.contributors.includes(contributor.login) ) @@ -74,11 +86,11 @@ export const getServerSideProps: GetServerSideProps = (async ({ res }) => { interface CareersPageProps { jobs: Record - placeholderJob: JobItemProps + placeholderJob: JobItemProps | null contributors: { login: string; avatar_url: string; html_url: string }[] } -const CareerPage: NextPage = ({ jobs, placeholderJob, contributors }) => { +const CareerPage = ({ jobs, placeholderJob, contributors }: CareersPageProps) => { const { basePath } = useRouter() const meta_title = 'Careers | Supabase' @@ -118,7 +130,7 @@ const CareerPage: NextPage = ({ jobs, placeholderJob, contribu
- {career.company.map((company: { number: string; text: string }, i: number) => { + {career.company.map((company, i) => { return (
= ({ jobs, placeholderJob, contribu our team effective:

- {career.humanPowered.map( - (human: { icon: string; title: string; text: string }, i: number) => { - return ( -
-
-

{human.title}

-

{human.text}

-
+ {career.humanPowered.map((human, i) => { + return ( +
+
+

{human.title}

+

{human.text}

- ) - } - )} +
+ ) + })}
@@ -312,7 +322,7 @@ const CareerPage: NextPage = ({ jobs, placeholderJob, contribu

- {contributors.map((contributor: any, i: number) => { + {contributors.map((contributor, i) => { return (
= ({ jobs, placeholderJob, contribu
- {career.benefits.map( - (benefits: { icon: string; title: string; text: string }, i: number) => { - return ( -
-
-

{benefits.title}

- - {benefits.text} - -
+ {career.benefits.map((benefits, i) => { + return ( +
+
+

{benefits.title}

+ {benefits.text}
- ) - } - )} +
+ ) + })}
@@ -401,7 +407,7 @@ const CareerPage: NextPage = ({ jobs, placeholderJob, contribu

- {career.hiring.map((hiring: { title: string; text: string }, i: number) => { + {career.hiring.map((hiring, i) => { return (
= ({ jobs, placeholderJob, contribu

{team}

- {(teamJobs as JobItemProps[]) - .filter((job: any) => !filterGenericJob(job)) - .map((job: JobItemProps) => ( + {teamJobs + .filter((job) => !filterGenericJob(job)) + .map((job) => ( ))}
@@ -481,7 +487,7 @@ const CareerPage: NextPage = ({ jobs, placeholderJob, contribu ) } -const JobItem: React.FC<{ job: JobItemProps }> = ({ job }) => { +const JobItem = ({ job }: { job: JobItemProps }) => { const isPlaceholderJob = job.id === PLACEHOLDER_JOB_ID return (