import { getTranslations, setRequestLocale } from 'next-intl/server'; import Link from 'next/link'; import { Search, ArrowLeft, ArrowRight, Download, Users, Layers, Sparkles, Terminal, Zap } from 'lucide-react'; import { Header } from '@/components/Header'; import { Footer } from '@/components/Footer'; import { HeroSearch } from '@/components/HeroSearch'; import { SkillCard } from '@/components/SkillCard'; import { createDb, categoryQueries, skillQueries, skills, sql } from '@skillhub/db'; import { formatCompactNumber } from '@/lib/format-number'; // Force dynamic rendering to fetch fresh data from database export const dynamic = 'force-dynamic'; // Get stats directly from database async function getStats() { try { const db = createDb(); // Browse-ready filter: exclude duplicates and aggregators const browseReady = sql`${skills.isDuplicate} = false AND (${skills.skillType} IS NULL OR ${skills.skillType} != 'aggregator')`; // Get total skills count (browse-ready, SKILL.md only) const skillsResult = await db .select({ count: sql`count(*)::int` }) .from(skills) .where(sql`${skills.sourceFormat} = 'skill.md' AND ${skills.isBlocked} = false AND ${browseReady}`); const totalSkills = skillsResult[0]?.count ?? 0; // Get total downloads (ALL skills — downloads are real user actions) const downloadsResult = await db .select({ sum: sql`coalesce(sum(${skills.downloadCount}), 0)::int` }) .from(skills); const totalDownloads = downloadsResult[0]?.sum ?? 0; // Get total categories const categories = await categoryQueries.getAll(db); const totalCategories = categories.length; // Get unique contributors (browse-ready skills only) const contributorsResult = await db .select({ count: sql`count(distinct ${skills.githubOwner})::int` }) .from(skills) .where(browseReady); const totalContributors = contributorsResult[0]?.count ?? 0; // Get total indexed skills (all, before curation) for curation note const totalIndexedResult = await db .select({ count: sql`count(*)::int` }) .from(skills) .where(sql`${skills.isBlocked} = false`); const totalIndexed = totalIndexedResult[0]?.count ?? 0; return { totalSkills, totalDownloads, totalCategories, totalContributors, totalIndexed, platforms: 5, }; } catch (error) { console.error('Error fetching stats:', error); return null; } } // Get featured skills directly from database async function getFeaturedSkills() { try { const db = createDb(); // Get featured skills, or top skills by popularity if none are featured let featuredSkills = await skillQueries.getFeatured(db, 6); if (featuredSkills.length === 0) { // Fallback to adaptive popularity with owner/repo diversity featuredSkills = await skillQueries.getFeaturedWithDiversity(db, 6, 2, 3); } return featuredSkills; } catch (error) { console.error('Error fetching featured skills:', error); return []; } } export default async function HomePage({ params, }: { params: Promise<{ locale: string }>; }) { const { locale } = await params; setRequestLocale(locale); const t = await getTranslations('home'); const tCommon = await getTranslations('common'); const isRTL = locale === 'fa'; const ArrowIcon = isRTL ? ArrowLeft : ArrowRight; // Fetch real data const [statsData, featuredSkills] = await Promise.all([ getStats(), getFeaturedSkills(), ]); const stats = [ { value: statsData ? formatCompactNumber(statsData.totalSkills, locale) : '۰', label: t('stats.skills'), icon: Layers }, { value: statsData ? formatCompactNumber(statsData.totalDownloads, locale) : '۰', label: t('stats.downloads'), icon: Download }, { value: statsData ? formatCompactNumber(statsData.totalContributors, locale) : '۰', label: t('stats.contributors'), icon: Users }, { value: statsData ? formatCompactNumber(statsData.totalCategories || 8, locale) : '۸', label: t('stats.categories'), icon: Sparkles }, ]; const steps = [ { icon: Search, title: t('howItWorks.step1.title'), description: t('howItWorks.step1.description'), }, { icon: Terminal, title: t('howItWorks.step2.title'), description: t('howItWorks.step2.description'), }, { icon: Zap, title: t('howItWorks.step3.title'), description: t('howItWorks.step3.description'), }, ]; return (
{/* Hero Section */}
{/* Tagline */}

{t('hero.tagline')}

{/* Title */}

{t('hero.title')}

{/* Subtitle */}

{t('hero.subtitle')}

{/* Search - Client Component */} {/* CTA Buttons */}
{t('hero.cta')} {t('hero.ctaSecondary')}
{/* Decorative elements */}
{/* Stats Section */}
{stats.map((stat, index) => (
{stat.value}
{stat.label}
))}

{t('stats.curationNote', { totalIndexed: formatCompactNumber(statsData?.totalIndexed ?? 0, locale) })}

{/* Featured Skills */}

{t('featured.title')}

{t('featured.subtitle')}

{featuredSkills.length > 0 ? ( featuredSkills.map((skill) => ( )) ) : ( // Fallback placeholder cards if no skills [1, 2, 3, 4, 5, 6].map((i) => (
)) )}
{tCommon('viewAll')}
{/* How It Works */}

{t('howItWorks.title')}

{steps.map((step, index) => (

{step.title}

{step.description}

))}
{/* CLI Example */}
$ npx skillhub install anthropics/skills/pdf ✓ Skill installed to ~/.claude/skills/pdf/
); }