diff --git a/.changeset/unlucky-lamps-reply.md b/.changeset/unlucky-lamps-reply.md new file mode 100644 index 0000000000..7e1195941c --- /dev/null +++ b/.changeset/unlucky-lamps-reply.md @@ -0,0 +1,5 @@ +--- +"@hyperdx/app": minor +--- + +Add careers page (/careers) with Greenhouse job listings filtered to HyperDX/ClickStack roles, GitHub commit activity feed, and a CTA in the AppNav sidebar for local mode diff --git a/packages/app/pages/careers.tsx b/packages/app/pages/careers.tsx new file mode 100644 index 0000000000..af7cd71e81 --- /dev/null +++ b/packages/app/pages/careers.tsx @@ -0,0 +1,2 @@ +import CareersPage from '@/CareersPage'; +export default CareersPage; diff --git a/packages/app/src/CareersPage.tsx b/packages/app/src/CareersPage.tsx new file mode 100644 index 0000000000..82c842761f --- /dev/null +++ b/packages/app/src/CareersPage.tsx @@ -0,0 +1,189 @@ +import { useMemo } from 'react'; +import Head from 'next/head'; +import { + Anchor, + Avatar, + Card, + Container, + Group, + Loader, + Stack, + Text, + Title, +} from '@mantine/core'; +import { useQuery } from '@tanstack/react-query'; + +interface GreenhouseJob { + id: number; + title: string; + absolute_url: string; + location: { name: string }; + updated_at: string; + departments: { id: number; name: string }[]; + content: string; +} + +interface GreenhouseResponse { + jobs: GreenhouseJob[]; +} + +interface GitHubCommit { + sha: string; + html_url: string; + commit: { + message: string; + author: { + name: string; + date: string; + }; + }; + author: { + login: string; + avatar_url: string; + } | null; +} + +const GITHUB_COMMITS_URL = + 'https://api.github.com/repos/hyperdxio/hyperdx/commits?sha=main&per_page=50'; + +const GREENHOUSE_API_URL = + 'https://boards-api.greenhouse.io/v1/boards/clickhouse/jobs'; + +const FILTER_PATTERN = /hyperdx|clickstack/i; + +function useRecentCommits() { + return useQuery({ + queryKey: ['github-commits'], + queryFn: async () => { + const res = await fetch(GITHUB_COMMITS_URL); + if (!res.ok) { + throw new Error('Failed to fetch commits'); + } + return res.json(); + }, + staleTime: 5 * 60 * 1000, + }); +} + +function useGreenhouseJobs() { + return useQuery({ + queryKey: ['greenhouse-jobs'], + queryFn: async () => { + const res = await fetch(GREENHOUSE_API_URL); + if (!res.ok) { + throw new Error('Failed to fetch job listings'); + } + return res.json(); + }, + staleTime: 5 * 60 * 1000, + }); +} + +export default function CareersPage() { + const { data: commitsData, isLoading: commitsLoading } = useRecentCommits(); + const { data, isLoading, isError } = useGreenhouseJobs(); + + const filteredJobs = useMemo(() => { + if (!data?.jobs) return []; + return data.jobs.filter(job => FILTER_PATTERN.test(job.title)); + }, [data]); + + return ( + + + Careers | HyperDX + + + + ClickHouse Careers: Help Build the Future of Observability + + + Join us to build ClickStack at ClickHouse, scaling a high performance + observability platform that ingests and queries petabytes of telemetry + across metrics, logs, and traces. +
+
+ Open positions are listed below. +
+ + {isLoading && } + + {isError && ( + + Unable to load job listings. Please try again later. + + )} + + {!isLoading && !isError && filteredJobs.length === 0 && ( + + No open positions at the moment. Check back soon! + + )} + + {filteredJobs.map(job => ( + + +
+ + {job.title} + + + {job.location.name} + +
+
+
+ ))} + + + Recent Activity + + + See what types of problems our team (and community) have been working + on lately. + + + {commitsLoading && } + + {commitsData?.map(commit => ( + + + + +
+ + {commit.commit.message.split('\n')[0]} + + + + {commit.author?.login ?? commit.commit.author.name} + + + {new Date(commit.commit.author.date).toLocaleDateString()} + + +
+
+
+
+ ))} +
+
+ ); +} diff --git a/packages/app/src/components/AppNav/AppNav.tsx b/packages/app/src/components/AppNav/AppNav.tsx index 3ab9fa1e0e..a18c9b918c 100644 --- a/packages/app/src/components/AppNav/AppNav.tsx +++ b/packages/app/src/components/AppNav/AppNav.tsx @@ -22,6 +22,7 @@ import { Input, Loader, ScrollArea, + Text, } from '@mantine/core'; import { useDisclosure, useLocalStorage } from '@mantine/hooks'; import { @@ -893,6 +894,21 @@ export default function AppNav({ fixed = false }: { fixed?: boolean }) { version={APP_VERSION} onAddDataClick={openInstallInstructions} /> + {IS_LOCAL_MODE && !isCollapsed && ( + + + Join us & build the future of high scale observability → + + + )}