diff --git a/apps/www/components/Nav/DevelopersDropdown.tsx b/apps/www/components/Nav/DevelopersDropdown.tsx
index db69f35ebc5..022725b7522 100644
--- a/apps/www/components/Nav/DevelopersDropdown.tsx
+++ b/apps/www/components/Nav/DevelopersDropdown.tsx
@@ -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 && }
{link.text}
+ {link.text === 'Careers' && jobsCount > 0 && (
+
+ ({jobsCount})
+
+ )}
(
- {blogPosts?.map((post) => (
+ {latestBlogPosts?.map((post) => (
-
{
- const [stars, setStars] = useState('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 (
diff --git a/apps/www/components/Nav/MobileMenu.tsx b/apps/www/components/Nav/MobileMenu.tsx
index ee7c686c4a5..bedfd7b9926 100644
--- a/apps/www/components/Nav/MobileMenu.tsx
+++ b/apps/www/components/Nav/MobileMenu.tsx
@@ -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"
/>
))}
diff --git a/apps/www/package.json b/apps/www/package.json
index c1ae0dd5a22..7d02406b3c2 100644
--- a/apps/www/package.json
+++ b/apps/www/package.json
@@ -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"
},
diff --git a/apps/www/public/rss.xml b/apps/www/public/rss.xml
index 0ad02776e2c..3f239826b71 100644
--- a/apps/www/public/rss.xml
+++ b/apps/www/public/rss.xml
@@ -42,20 +42,6 @@
Technical deep dive into the new DBOS integration for Supabase
Tue, 10 Dec 2024 00:00:00 -0700
-
-
- https://supabase.com/blog/database-build-v2
- database.build v2: Bring-your-own-LLM
- https://supabase.com/blog/database-build-v2
- Use any OpenAI API compatible LLMs in database.build
- Fri, 06 Dec 2024 00:00:00 -0700
-
--
- https://supabase.com/blog/restore-to-a-new-project
- Restore to a New Project
- https://supabase.com/blog/restore-to-a-new-project
- Effortlessly Clone Data into a New Supabase Project
- Fri, 06 Dec 2024 00:00:00 -0700
-
-
https://supabase.com/blog/hack-the-base
Hack the Base! with Supabase
@@ -70,6 +56,20 @@
Highlights from Launch Week 13
Fri, 06 Dec 2024 00:00:00 -0700
+-
+ https://supabase.com/blog/database-build-v2
+ database.build v2: Bring-your-own-LLM
+ https://supabase.com/blog/database-build-v2
+ Use any OpenAI API compatible LLMs in database.build
+ Fri, 06 Dec 2024 00:00:00 -0700
+
+-
+ https://supabase.com/blog/restore-to-a-new-project
+ Restore to a New Project
+ https://supabase.com/blog/restore-to-a-new-project
+ Effortlessly Clone Data into a New Supabase Project
+ Fri, 06 Dec 2024 00:00:00 -0700
+
-
https://supabase.com/blog/supabase-queues
Supabase Queues
diff --git a/apps/www/scripts/generateLatestBlogPosts.mjs b/apps/www/scripts/generateStaticContent.mjs
similarity index 59%
rename from apps/www/scripts/generateLatestBlogPosts.mjs
rename to apps/www/scripts/generateStaticContent.mjs
index 3a8a36f3cfe..0fbfdeaf9cc 100644
--- a/apps/www/scripts/generateLatestBlogPosts.mjs
+++ b/apps/www/scripts/generateStaticContent.mjs
@@ -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'
+)
diff --git a/packages/ui/src/components/TextLink/TextLink.tsx b/packages/ui/src/components/TextLink/TextLink.tsx
index 11a49169456..0df330769ba 100644
--- a/packages/ui/src/components/TextLink/TextLink.tsx
+++ b/packages/ui/src/components/TextLink/TextLink.tsx
@@ -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({
{`${label} about ${url}`}
{label}
+ {counter && (
+
+ ({counter})
+
+ )}
{hasChevron && (