Files
ai-learning-platform/frontend/src/app/page.tsx
T
yuzhiran-dev 477226ac15 feat: 剩余页面 useT() 改造 + 首页课程动态加载
- 搜索/发现/工具/My 页面全部使用 useT(),支持中英切换
- 搜索页 Badge variant 修正
- 首页热门专题改为从 API 动态获取(无数据时用静态兜底)
- 新增 discover 翻译键 viewCount/likeCount/postStats
- 首页/仪表盘/社区 等 JSX 优化
2026-05-22 17:08:17 +08:00

168 lines
9.9 KiB
TypeScript

'use client';
import Link from 'next/link';
import { useState, useEffect } from 'react';
import { HomePageClient } from './home-client';
import { ArrowRight, Sparkles, BookOpen, Bot, Compass, Zap } from 'lucide-react';
import { useT } from '@/i18n';
import { API_BASE } from '@/lib/config';
export default function HomePage() {
const t = useT();
const [courses, setCourses] = useState<any[]>([]);
useEffect(() => {
fetch(`${API_BASE}/courses?pageSize=3`)
.then(r => r.json()).then(data => setCourses(data.items || []))
.catch(() => {});
}, []);
const stats = [
{ value: '50+', label: t.home.statTopics },
{ value: '200+', label: t.home.statPrompts },
{ value: '30+', label: t.home.statTools },
{ value: '10,000+', label: t.home.statExplorers },
];
const features = [
{ icon: Compass, title: t.home.featureGuide, desc: t.home.featureGuideDesc },
{ icon: Bot, title: t.home.featureSandbox, desc: t.home.featureSandboxDesc },
{ icon: BookOpen, title: t.home.featurePrompts, desc: t.home.featurePromptsDesc },
{ icon: Zap, title: t.home.featureUpdate, desc: t.home.featureUpdateDesc },
];
return (
<HomePageClient>
{/* Hero */}
<section className="relative overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-br from-brand-50 via-white to-blue-50 dark:from-brand-950/30 dark:via-background dark:to-blue-950/20" />
<div className="absolute inset-0 bg-[url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjAiIGhlaWdodD0iNjAiIHZpZXdCb3g9IjAgMCA2MCA2MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxnIGZpbGw9IiM2NjdlZWEiIGZpbGwtb3BhY2l0eT0iMC4wNCI+PHBhdGggZD0iTTM2IDM0djItSDI0di0yaDEyek0zNiAyNHYySDI0di0yaDEyeiIvPjwvZz48L2c+PC9zdmc+')] opacity-50 dark:opacity-20" />
<div className="absolute top-20 right-0 w-96 h-96 bg-brand-400/10 dark:bg-brand-400/5 rounded-full blur-3xl" />
<div className="absolute bottom-0 left-20 w-72 h-72 bg-blue-400/10 dark:bg-blue-400/5 rounded-full blur-3xl" />
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-20 md:py-28 relative">
<div className="text-center max-w-3xl mx-auto animate-fade-in-up">
<span className="inline-flex items-center gap-1.5 px-4 py-1.5 text-sm font-medium text-brand-700 dark:text-brand-300 bg-brand-100 dark:bg-brand-900/50 rounded-full mb-8 border border-brand-200 dark:border-brand-800">
<Sparkles className="w-3.5 h-3.5" />
{t.home.badge}
</span>
<h1 className="text-4xl md:text-6xl font-bold tracking-tight leading-tight">
<span className="bg-gradient-to-r from-brand-600 via-brand-500 to-blue-500 bg-clip-text text-transparent">
{t.home.heroHighlight}
</span>
<br />{t.home.heroRest}
</h1>
<p className="mt-6 text-lg md:text-xl text-muted-foreground leading-relaxed max-w-2xl mx-auto">
{t.home.desc}
</p>
<div className="mt-10 flex flex-col sm:flex-row gap-4 justify-center">
<Link href="/courses" className="inline-flex items-center justify-center gap-2 px-8 py-3 text-base font-medium text-white bg-brand-600 rounded-xl hover:bg-brand-700 transition-all shadow-lg shadow-brand-200/50 dark:shadow-brand-900/30 hover:shadow-xl hover:-translate-y-0.5 active:scale-[0.98]">
<BookOpen className="w-5 h-5" />
{t.home.startExplore}
<ArrowRight className="w-4 h-4" />
</Link>
<Link href="/auth?tab=register" className="inline-flex items-center justify-center gap-2 px-8 py-3 text-base font-medium text-foreground bg-card border border-border rounded-xl hover:bg-accent transition-all hover:-translate-y-0.5 active:scale-[0.98] shadow-sm">
{t.home.freeRegister}
</Link>
</div>
</div>
</div>
</section>
{/* Stats */}
<section className="py-12 md:py-16 border-y border-border">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="grid grid-cols-2 md:grid-cols-4 gap-8">
{stats.map((s) => (
<div key={s.label} className="text-center group">
<div className="text-3xl md:text-4xl font-bold bg-gradient-to-b from-brand-600 to-brand-400 bg-clip-text text-transparent group-hover:scale-110 transition-transform">
{s.value}
</div>
<div className="mt-1.5 text-sm text-muted-foreground">{s.label}</div>
</div>
))}
</div>
</div>
</section>
{/* Features */}
<section className="py-20">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center mb-16">
<h2 className="text-3xl font-bold">{t.home.whyTitle}</h2>
<p className="mt-4 text-lg text-muted-foreground">{t.home.whyDesc}</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{features.map((feat) => (
<div key={feat.title} className="group text-center p-8 rounded-2xl bg-card border border-border hover:border-brand-200 dark:hover:border-brand-800 transition-all hover:-translate-y-1 hover:shadow-lg">
<div className="w-14 h-14 bg-brand-100 dark:bg-brand-900/30 rounded-2xl flex items-center justify-center mx-auto mb-5 group-hover:scale-110 transition-transform">
<feat.icon className="w-7 h-7 text-brand-600 dark:text-brand-400" />
</div>
<h3 className="text-lg font-semibold mb-2">{feat.title}</h3>
<p className="text-sm text-muted-foreground leading-relaxed">{feat.desc}</p>
</div>
))}
</div>
</div>
</section>
{/* Popular Courses */}
<section className="py-20 bg-muted/30">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between mb-10">
<div>
<h2 className="text-3xl font-bold">{t.home.popularTopics}</h2>
<p className="mt-2 text-muted-foreground">{t.home.popularDesc}</p>
</div>
<Link href="/courses" className="hidden sm:inline-flex items-center gap-1 text-brand-600 hover:text-brand-700 font-medium text-sm group">
{t.common.viewAll} <ArrowRight className="w-4 h-4 group-hover:translate-x-0.5 transition-transform" />
</Link>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{courses.length > 0 ? courses.map((course: any) => (
<Link key={course.id} href={`/courses/${course.id}`} className="block group">
<div className="group bg-card rounded-xl border border-border overflow-hidden hover:shadow-xl transition-all hover:-translate-y-1">
<div className="h-2 bg-gradient-to-r from-brand-500 to-blue-500" />
<div className="p-6">
<div className="flex items-center justify-between mb-3">
<span className="text-xs font-medium text-brand-700 dark:text-brand-300 bg-brand-50 dark:bg-brand-900/30 px-2 py-1 rounded-full">{course.isFree ? t.courses.free : t.courses.paid}</span>
</div>
<h3 className="text-lg font-semibold mb-2 group-hover:text-brand-600 transition-colors">{course.title}</h3>
<p className="text-sm text-muted-foreground mb-4 line-clamp-2">{course.description || ''}</p>
</div>
</div>
</Link>
)) : (
<>
{[{ title: 'AI 通识:零基础入门', tag: t.courses.free }, { title: '提示词工程从入门到精通', tag: '热门' }, { title: '用 AI 提升 10 倍办公效率', tag: '推荐' }].map((course) => (
<div key={course.title} className="group bg-card rounded-xl border border-border overflow-hidden hover:shadow-xl transition-all hover:-translate-y-1">
<div className="h-2 bg-gradient-to-r from-brand-500 to-blue-500" />
<div className="p-6">
<div className="flex items-center justify-between mb-3">
<span className="text-xs font-medium text-brand-700 dark:text-brand-300 bg-brand-50 dark:bg-brand-900/30 px-2 py-1 rounded-full">{course.tag}</span>
</div>
<h3 className="text-lg font-semibold mb-2 group-hover:text-brand-600 transition-colors">{course.title}</h3>
<p className="text-sm text-muted-foreground mb-4 line-clamp-2"> AI </p>
</div>
</div>
))}
</>
)}
</div>
</div>
</section>
{/* CTA */}
<section className="py-20 bg-gradient-to-r from-brand-600 to-brand-800 dark:from-brand-900 dark:to-brand-950 relative overflow-hidden">
<div className="absolute inset-0 bg-[url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMjAgMjB2MTBoLTEwVjIwaDEwek0yMCAwaDEwdjEwSDIwVjB6IiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9IjAuMDMiLz48L3N2Zz4=')] opacity-50" />
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 text-center relative">
<h2 className="text-3xl font-bold text-white mb-4">{t.home.ctaTitle}</h2>
<p className="text-brand-100/80 dark:text-brand-200/80 mb-8 text-lg">{t.home.ctaDesc}</p>
<Link href="/auth?tab=register" className="inline-flex items-center gap-2 px-8 py-3 text-base font-medium text-brand-600 bg-white rounded-xl hover:bg-brand-50 transition-all hover:-translate-y-0.5 active:scale-[0.98] shadow-xl">
{t.home.freeRegister}
<ArrowRight className="w-4 h-4" />
</Link>
</div>
</section>
</HomePageClient>
);
}