feat: careers jobsCount in nav (#32771)

* add jobsCount to careers nav

* badge styling

* mobile career counter

* refactor static content generation

* refactor

* fix gh number formatting

* remove comment

* remove component

* fix fresh local dev & update spacing
This commit is contained in:
Francesco Sansalvadore authored and GitHub committed 2025-01-15 18:03:22 +01:00
1 parent 8a648073de
commit 592af2ffa2
7 files changed
+100 -46

No files matched your search

+10 -2
View File
@@ -2,7 +2,10 @@ import { ChevronRight } from 'lucide-react'
import Link from 'next/link'
import { data as DevelopersData } from 'data/Developers'
import blogPosts from '~/.contentlayer/generated/LatestBlogPost/_index.json' with { type: 'json' }
import {
jobsCount,
latestBlogPosts,
} from '~/.contentlayer/generated/staticContent/_index.json' with { type: 'json' }
type LinkProps = {
text: string
@@ -29,6 +32,11 @@ const DevelopersDropdown = () => (
>
{Icon && <Icon size={16} strokeWidth={1.3} />}
<span>{link.text}</span>
{link.text === 'Careers' && jobsCount > 0 && (
<span className="text-xs flex items-center justify-center text-foreground-lighter -ml-1">
({jobsCount})
</span>
)}
<ChevronRight
strokeWidth={2}
className="w-3 -ml-1 transition-all will-change-transform -translate-x-1 opacity-0 group-hover:translate-x-0 group-hover:opacity-100"
@@ -50,7 +58,7 @@ const DevelopersDropdown = () => (
<ChevronRight className="h-3 w-3 transition-transform will-change-transform -translate-x-1 group-hover:translate-x-0" />
</Link>
<ul className="flex flex-col gap-5">
{blogPosts?.map((post) => (
{latestBlogPosts?.map((post) => (
<li key={post.title}>
<Link
href={post.url}
+16 -24
View File
@@ -1,33 +1,25 @@
import React, { useEffect, useState } from 'react'
import React from 'react'
import { Button } from 'ui'
import { githubStars } from '~/.contentlayer/generated/staticContent/_index.json' with { type: 'json' }
const GitHubButton = () => {
const [stars, setStars] = useState<string>('63')
const kFormatter = (num: number) => {
const kFormat = num / 1000
const decimals = kFormat.toFixed(2).split('.')[1]
const firstDecimal = decimals.split('')[0]
const showDecimals = firstDecimal !== '0'
const kFormat = Math.floor(num / 1000)
const lastTwoDigits = num % 1000
return kFormat.toFixed(showDecimals ? 1 : 0)
const decimalPart = Math.floor((lastTwoDigits % 100) / 10)
const hundreds = Math.floor(lastTwoDigits / 100)
const isAlmostNextThousand = decimalPart >= 8 && hundreds >= 9
const showDecimals =
(!isAlmostNextThousand && hundreds >= 1) || (hundreds === 0 && decimalPart >= 8)
return showDecimals
? `${kFormat}.${decimalPart >= 8 ? hundreds + 1 : hundreds}K`
: `${isAlmostNextThousand ? kFormat + 1 : kFormat}K`
}
useEffect(() => {
async function fetchOctoData() {
const { Octokit } = await import('@octokit/core')
const octokit = new Octokit()
const res = await octokit.request('GET /repos/{org}/{repo}', {
org: 'supabase',
repo: 'supabase',
type: 'public',
})
setStars(kFormatter(res.data?.stargazers_count))
}
fetchOctoData()
}, [])
return (
<Button
className="hidden group lg:flex text-foreground-light hover:text-foreground"
@@ -49,7 +41,7 @@ const GitHubButton = () => {
fill="currentColor"
/>
</svg>
{stars}K
{kFormatter(githubStars)}
</span>
</a>
</Button>
+2
View File
@@ -13,6 +13,7 @@ import { useKey } from 'react-use'
import { useIsLoggedIn, useIsUserLoading } from 'common'
import { ChevronRight } from 'lucide-react'
import ProductModulesData from '~/data/ProductModules'
import { jobsCount } from '~/.contentlayer/generated/staticContent/_index.json' with { type: 'json' }
interface Props {
open: boolean
@@ -105,6 +106,7 @@ const MobileMenu = ({ open, setOpen, menu }: Props) => {
key={link.text}
url={link.url}
label={link.text}
counter={link.text === 'Careers' && jobsCount > 0 ? jobsCount : undefined}
className="focus-visible:ring-offset-4 focus-visible:ring-offset-background-overlay !mt-0"
/>
))}
+2 -2
View File
@@ -5,14 +5,14 @@
"private": true,
"scripts": {
"preinstall": "npx only-allow pnpm",
"dev": "next --port 3000",
"dev": "pnpm run content:build && next --port 3000",
"build": "pnpm run content:build && next build",
"export": "next export",
"start": "next start",
"lint": "next lint",
"clean": "rimraf node_modules",
"typecheck": "pnpm run content:build && tsc --noEmit",
"content:build": "contentlayer2 build && node scripts/generateLatestBlogPosts.mjs",
"content:build": "contentlayer2 build && node scripts/generateStaticContent.mjs",
"prettier": "prettier --write \"./{pages,components,lib,stores,styles,tests}/**/*.{ts,tsx,md,js,jsx,json}\"",
"postbuild": "node ./internals/generate-sitemap.mjs"
},
+14 -14
View File
@@ -42,20 +42,6 @@
<description>Technical deep dive into the new DBOS integration for Supabase</description>
<pubDate>Tue, 10 Dec 2024 00:00:00 -0700</pubDate>
</item>
<item>
<guid>https://supabase.com/blog/database-build-v2</guid>
<title>database.build v2: Bring-your-own-LLM</title>
<link>https://supabase.com/blog/database-build-v2</link>
<description>Use any OpenAI API compatible LLMs in database.build</description>
<pubDate>Fri, 06 Dec 2024 00:00:00 -0700</pubDate>
</item>
<item>
<guid>https://supabase.com/blog/restore-to-a-new-project</guid>
<title>Restore to a New Project</title>
<link>https://supabase.com/blog/restore-to-a-new-project</link>
<description>Effortlessly Clone Data into a New Supabase Project</description>
<pubDate>Fri, 06 Dec 2024 00:00:00 -0700</pubDate>
</item>
<item>
<guid>https://supabase.com/blog/hack-the-base</guid>
<title>Hack the Base! with Supabase</title>
@@ -70,6 +56,20 @@
<description>Highlights from Launch Week 13</description>
<pubDate>Fri, 06 Dec 2024 00:00:00 -0700</pubDate>
</item>
<item>
<guid>https://supabase.com/blog/database-build-v2</guid>
<title>database.build v2: Bring-your-own-LLM</title>
<link>https://supabase.com/blog/database-build-v2</link>
<description>Use any OpenAI API compatible LLMs in database.build</description>
<pubDate>Fri, 06 Dec 2024 00:00:00 -0700</pubDate>
</item>
<item>
<guid>https://supabase.com/blog/restore-to-a-new-project</guid>
<title>Restore to a New Project</title>
<link>https://supabase.com/blog/restore-to-a-new-project</link>
<description>Effortlessly Clone Data into a New Supabase Project</description>
<pubDate>Fri, 06 Dec 2024 00:00:00 -0700</pubDate>
</item>
<item>
<guid>https://supabase.com/blog/supabase-queues</guid>
<title>Supabase Queues</title>
@@ -1,6 +1,8 @@
import { promises as fs } from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'
// Latest Blog Posts
import { allBlogPosts } from '../.contentlayer/generated/index.mjs'
/**
@@ -27,8 +29,46 @@ const latestBlogPosts = allBlogPosts
.slice(0, 2)
.map(({ title, url, description }) => ({ title, url, description }))
let stars = 0
// GitHub Stars
const fetchOctoData = async () => {
const { Octokit } = await import('@octokit/core')
const octokit = new Octokit()
const res = await octokit.request('GET /repos/{org}/{repo}', {
org: 'supabase',
repo: 'supabase',
type: 'public',
})
return res.data?.stargazers_count
}
try {
stars = await fetchOctoData()
} catch (error) {
throw error
}
// Careers Jobs count
const getCareerCount = async () => {
const job_res = await fetch('https://api.ashbyhq.com/posting-api/job-board/supabase')
const job_data = await job_res.json()
return job_data.jobs.length
}
let careersCount = 0
try {
careersCount = await getCareerCount()
} catch (error) {
throw error
}
// Create folder for static content
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const folderPath = path.join(__dirname, '../.contentlayer/generated/LatestBlogPost')
const folderPath = path.join(__dirname, '../.contentlayer/generated/staticContent')
try {
await fs.mkdir(folderPath, { recursive: true })
} catch (error) {
@@ -38,5 +78,10 @@ try {
// Folder already exists, continue silently
}
const filePath = path.join(__dirname, '../.contentlayer/generated/LatestBlogPost/_index.json')
await fs.writeFile(filePath, JSON.stringify(latestBlogPosts), 'utf8')
// Write static content to file
const filePath = path.join(__dirname, '../.contentlayer/generated/staticContent/_index.json')
await fs.writeFile(
filePath,
JSON.stringify({ latestBlogPosts: latestBlogPosts, jobsCount: careersCount, githubStars: stars }),
'utf8'
)
@@ -8,6 +8,7 @@ interface Props {
label: string
url?: string
className?: string
counter?: number
hasChevron?: boolean
chevronAnimation?: 'translate' | 'fadeIn'
target?: '_blank' | '_self'
@@ -17,6 +18,7 @@ function TextLink({
url = '',
label,
className,
counter,
hasChevron = true,
chevronAnimation = 'translate',
target = '_self',
@@ -26,7 +28,7 @@ function TextLink({
<Link
href={url}
className={cn(
'text-foreground-light hover:text-foreground mt-3 block cursor-pointer text-sm focus-visible:ring-2 focus-visible:outline-none focus-visible:rounded-sm focus-visible:ring-foreground-lighter focus-visible:text-foreground',
'group/text-link text-foreground-light hover:text-foreground mt-3 block cursor-pointer text-sm focus-visible:ring-2 focus-visible:outline-none focus-visible:rounded-sm focus-visible:ring-foreground-lighter focus-visible:text-foreground',
className
)}
target={target}
@@ -35,6 +37,11 @@ function TextLink({
<div className="group flex items-center gap-1">
<span className="sr-only">{`${label} about ${url}`}</span>
<span>{label}</span>
{counter && (
<span className="text-xs flex items-center justify-center text-foreground-lighter group-hover/text-link:text-foreground">
({counter})
</span>
)}
{hasChevron && (
<div
className={cn(