mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 01:45:10 +03:00
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:
1 parent
8a648073de
commit
592af2ffa2
7 files changed
+100
-46
No files matched your search
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
/>
|
||||
))}
|
||||
|
||||
@@ -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
@@ -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>
|
||||
|
||||
+48
-3
@@ -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(
|
||||
|
||||
Reference in new issue
Block a user