{post.category}{minutesLabel(post.readingMinutes)}{post.access === 'free' ? 'مجاني' : 'للأعضاء'}
{post.title}
{post.dek}
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 } from '@/lib/launch' export const dynamic = 'force-dynamic' /* Editorial picks for «ابدأ من هنا». */ const START_HERE = ['first-arabic-agent', 'choosing-your-model', 'prototype-to-production', 'simple-arabic-rag', 'safe-agent-guardrails', 'agent-loop-stop-condition'] type Doc = { id: number; title: string; slug: string; dek?: string | null; access: 'free' | 'vip'; category: string; level?: string | null; readingMinutes: number; 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])) const startHere = START_HERE.map(slug => docs.find(d => d.slug === slug)).filter((d): d is Doc => Boolean(d)) return (
مكتبة عربية للذكاء الاصطناعي العملي
أدلة عربية عملية عن الوكلاء والنماذج والأتمتة، يكتبها مهندسو بنية تحتية لمن يريد أن يشغّل شيئاً حقيقياً.
كل دليل ينتمي إلى مسار، وأدلة المبتدئين تظهر أيضاً في الأساسيات.
{post.category}{minutesLabel(post.readingMinutes)}{post.access === 'free' ? 'مجاني' : 'للأعضاء'}
{post.dek}
الأدلة المجانية كاملة دائماً. العضوية تضيف الأدلة المعمّقة، القوالب الجاهزة للتنزيل، والنقاش مع الفريق.
وصول مبكر مجاني
يمكنك قراءة المحتوى المنشور خلال الوصول المبكر دون بطاقة دفع.
> ) : ({MEMBERSHIP_PRICE} شهرياً، وتلغي متى شئت
)}