import { getViewer } from '@/lib/viewer' import Topnav from '@/components/topnav' import Footer from '@/components/footer' import Link from 'next/link' import { getPayload } from 'payload' import configPromise from '@payload-config' import { TOPICS, topicsFor, INTEREST_MAP } from '@/lib/topics' import { formatNumber, guidesLabel, minutesLabel, MEMBERSHIP_PRICE } from '@/lib/format' import { freeEarlyAccess, accessLabel, LAUNCH_POLICY } from '@/lib/launch' export const dynamic = 'force-dynamic' /* Demo seed articles remain in the library, but never in the popular section. */ const DEMO_POST_SLUGS = ['first-arabic-agent', 'agent-loop-stop-condition', 'choosing-your-model', 'systematic-model-eval', 'simple-arabic-rag', 'vector-databases-in-practice', 'safe-agent-guardrails', 'agent-output-review-gates', 'indispensable-ai-tools', 'tool-pipelines-and-permissions', 'ai-in-your-business', 'prototype-to-production', 'article-agent-guide'] type Doc = { id: number; title: string; slug: string; dek?: string | null; access: 'free' | 'vip'; category: string; level?: string | null; readingMinutes: number; views?: number | null; cover?: { url?: string | null } | number | null } export default async function HomePage() { const viewer = await getViewer() const payload = await getPayload({ config: configPromise }) const { docs: raw } = await payload.find({ collection: 'posts', where: { status: { equals: 'published' } }, depth: 1, limit: 200, sort: '-publishedAt', select: { title: true, slug: true, dek: true, access: true, category: true, level: true, readingMinutes: true, cover: true }, }) const docs = raw as unknown as Doc[] const freeCount = docs.filter(d => d.access === 'free').length // Hero personalization: matching the viewer's interest floats to the top, // level match ranks higher, ties keep the -publishedAt order. Anonymous // viewers get the plain newest-first list. const interestTopic = viewer?.interest ? INTEREST_MAP[viewer.interest] : undefined const score = (d: Doc) => { if (!interestTopic || !topicsFor(d).includes(interestTopic)) return 0 return d.level === viewer?.level ? 2 : 1 } const hero = docs .map((d, i) => ({ d, i, s: score(d) })) .sort((a, b) => b.s - a.s || a.i - b.i) .slice(0, 4) .map(({ d }) => d) const counts = Object.fromEntries(TOPICS.map(t => [t.key, docs.filter(d => topicsFor(d).includes(t.key)).length])) // Query the complete published collection, not just the 200 newest posts. // Read-count ties prefer the newest publication, then a stable slug order. const { docs: popular } = await payload.find({ collection: 'posts', where: { and: [{ status: { equals: 'published' } }, { slug: { not_in: DEMO_POST_SLUGS } }] }, depth: 1, limit: 6, sort: ['-views', '-publishedAt', 'slug'], select: { title: true, slug: true, dek: true, access: true, category: true, readingMinutes: true, cover: true, views: true }, }) const startHere = popular as unknown as Doc[] return (
{/* Organization structured data (home page only; WebSite LD lives in layout). */}