feat: 实现 Agent Park AI 项目导航网站核心功能

完成 Next.js 14+ 全栈应用的 MVP 实现,包含以下功能:

## 项目设置
- 初始化 Next.js 14+ 项目,配置 TypeScript 严格模式
- 配置 Tailwind CSS、ESLint、Prettier 代码质量工具
- 集成 shadcn/ui 组件库和 next-intl 国际化方案

## 数据层
- 配置 PostgreSQL + Prisma ORM
- 定义 Project、Tag、ExternalLink 数据模型
- 实现种子数据脚本(5个AI项目)

## 用户故事 1:浏览和搜索 AI 项目
- 实现首页,展示热门标签云和精选项目
- 实现项目列表页,支持搜索和标签筛选
- 创建 TagCloud、ProjectCard、ProjectList、SearchBar 组件
- 实现 ISR 缓存策略优化性能

## 用户故事 2:查看项目详细信息
- 实现项目详情页,显示完整信息
- 创建 ExternalLinkCard 组件展示外部链接
- 实现安全的 target="_blank" 外链跳转

## 用户故事 4:数据更新和管理
- 实现 webhook API 端点接收 n8n 数据推送
- 实现 API Key 身份验证
- 支持部分成功模式的批量数据处理
- 完善的错误处理和日志记录

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-12-25 15:12:09 +08:00
co-authored by Claude
parent 6d40d4e8e6
commit ba1fab65c6
34 changed files with 8225 additions and 0 deletions
+68
View File
@@ -0,0 +1,68 @@
import { notFound } from 'next/navigation'
import { getProjectBySlug } from '@/hooks/useProjects'
import { ExternalLinkCard } from '@/components/project/ExternalLinkCard'
interface ProjectDetailPageProps {
params: Promise<{ locale: string; id: string }>
}
export default async function ProjectDetailPage({
params,
}: ProjectDetailPageProps) {
const { locale, id } = await params
const project = await getProjectBySlug(id)
if (!project) {
notFound()
}
return (
<div className="container mx-auto px-4 py-8">
<div className="max-w-4xl mx-auto">
<div className="mb-8">
<a
href={`/${locale}/projects`}
className="text-primary hover:underline text-sm"
>
← 返回项目列表
</a>
</div>
<div className="mb-8">
<h1 className="text-3xl font-bold mb-4">{project.name}</h1>
{project.nameEn && (
<h2 className="text-xl text-muted-foreground mb-4">{project.nameEn}</h2>
)}
<p className="text-lg text-muted-foreground">{project.description}</p>
</div>
{project.content && (
<div className="mb-8">
<h3 className="text-xl font-semibold mb-4">详细介绍</h3>
<div className="prose max-w-none">
<p>{project.content}</p>
</div>
</div>
)}
<div className="mb-8">
<h3 className="text-xl font-semibold mb-4">标签</h3>
<div className="flex flex-wrap gap-2">
{project.tags.map((tag) => (
<span
key={tag.id}
className="inline-flex items-center px-3 py-1 rounded-full text-sm bg-secondary text-secondary-foreground"
>
{tag.name}
</span>
))}
</div>
</div>
<ExternalLinkCard links={project.links} />
</div>
</div>
)
}
export const revalidate = 300 // ISR: revalidate every 5 minutes