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 && (