fix: 修复 AI 搜索跳转后 async/await 客户端组件错误
将 ProjectCard 和 SubmitProjectCard 从 async 组件重构为普通组件, 通过 props 接收翻译文本,解决在客户端组件中使用 async 组件导致的错误。 - 移除 ProjectCard 和 SubmitProjectCard 的 async 关键字 - 添加 translations prop 接收翻译文本 - 更新所有调用点传递翻译文本 Co-Authored-By: Claude (glm-4.7) <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,12 @@ interface ProjectsPageClientProps {
|
||||
searching: string
|
||||
aiHint: string
|
||||
traditionalHint: string
|
||||
translations: {
|
||||
viewDetails: string
|
||||
submitProjectTitle: string
|
||||
submitProjectDescription: string
|
||||
submitNow: string
|
||||
}
|
||||
}
|
||||
|
||||
export function ProjectsPageClient({
|
||||
@@ -38,6 +44,7 @@ export function ProjectsPageClient({
|
||||
searching,
|
||||
aiHint,
|
||||
traditionalHint,
|
||||
translations,
|
||||
}: ProjectsPageClientProps) {
|
||||
const searchParams = useSearchParams()
|
||||
const [aiResults, setAiResults] = useState<AISearchResult[]>([])
|
||||
@@ -150,7 +157,7 @@ export function ProjectsPageClient({
|
||||
<p className="text-red-700 dark:text-red-400 font-display text-sm">{error}</p>
|
||||
</div>
|
||||
)}
|
||||
<AISearchResults results={aiResults} locale={locale} />
|
||||
<AISearchResults results={aiResults} locale={locale} translations={translations} />
|
||||
</>
|
||||
) : (
|
||||
children
|
||||
|
||||
@@ -16,6 +16,7 @@ export default async function ProjectsPage({
|
||||
}: ProjectsPageProps) {
|
||||
const t = await getTranslations('home')
|
||||
const tCommon = await getTranslations('common')
|
||||
const tProject = await getTranslations('project')
|
||||
|
||||
const [resolvedParams, resolvedSearchParams] = await Promise.all([params, searchParams])
|
||||
const { locale } = resolvedParams
|
||||
@@ -29,6 +30,14 @@ export default async function ProjectsPage({
|
||||
getAllTags(), // 获取所有标签(用于展开)
|
||||
])
|
||||
|
||||
// 准备翻译文本传递给客户端组件
|
||||
const translations = {
|
||||
viewDetails: tCommon('viewDetails'),
|
||||
submitProjectTitle: tProject('submitProjectTitle'),
|
||||
submitProjectDescription: tProject('submitProjectDescription'),
|
||||
submitNow: tProject('submitNow'),
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="container mx-auto px-4 py-12 md:py-16 max-w-7xl">
|
||||
{/* Search and Filter Section */}
|
||||
@@ -45,6 +54,7 @@ export default async function ProjectsPage({
|
||||
searching={tCommon('searching')}
|
||||
aiHint={tCommon('aiHint')}
|
||||
traditionalHint={tCommon('traditionalHint')}
|
||||
translations={translations}
|
||||
>
|
||||
{/* Tag Cloud - rendered inside client component for non-AI mode */}
|
||||
<div className="border-t-2 border-gray-100 dark:border-gray-800 pt-6 mt-8">
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import Link from 'next/link'
|
||||
import Image from 'next/image'
|
||||
import { getTranslations } from 'next-intl/server'
|
||||
import { GitHubStatsCompact } from './GitHubBadges'
|
||||
import { getGitHubBadgesFromLinks } from '@/lib/github/badges'
|
||||
|
||||
@@ -27,6 +26,10 @@ interface ProjectCardProps {
|
||||
}
|
||||
locale: string
|
||||
featured?: boolean
|
||||
// 新增:通过 props 接收翻译文本,支持客户端组件使用
|
||||
translations?: {
|
||||
viewDetails: string
|
||||
}
|
||||
}
|
||||
|
||||
// Icon mapping for projects based on tags
|
||||
@@ -42,8 +45,9 @@ function getProjectIcon(tags: Array<{ name: string | null }>): string {
|
||||
return '✨'
|
||||
}
|
||||
|
||||
export async function ProjectCard({ project, locale, featured = false }: ProjectCardProps) {
|
||||
const t = await getTranslations('common')
|
||||
export function ProjectCard({ project, locale, featured = false, translations }: ProjectCardProps) {
|
||||
// 使用传入的翻译文本,如果没有提供则使用默认值
|
||||
const viewDetailsText = translations?.viewDetails || 'View Details'
|
||||
const icon = getProjectIcon(project.tags)
|
||||
const displayName = locale === 'en' && project.nameEn ? project.nameEn : project.name
|
||||
const displayDescription = locale === 'en' && project.descriptionEn ? project.descriptionEn : project.description
|
||||
@@ -94,7 +98,7 @@ export async function ProjectCard({ project, locale, featured = false }: Project
|
||||
href={`/${locale}/projects/${project.slug}`}
|
||||
className="inline-flex items-center font-display font-bold text-sm uppercase hover:text-secondary dark:hover:text-primary group"
|
||||
>
|
||||
{t('viewDetails')}{' '}
|
||||
{viewDetailsText}{' '}
|
||||
<span className="ml-1 transform group-hover:translate-x-1 transition-transform">→</span>
|
||||
</Link>
|
||||
|
||||
@@ -121,22 +125,34 @@ export async function ProjectCard({ project, locale, featured = false }: Project
|
||||
}
|
||||
|
||||
// Special card for "Submit Your Project" CTA
|
||||
export async function SubmitProjectCard({ locale }: { locale: string }) {
|
||||
const t = await getTranslations('project')
|
||||
interface SubmitProjectCardProps {
|
||||
locale: string
|
||||
translations?: {
|
||||
submitProjectTitle: string
|
||||
submitProjectDescription: string
|
||||
submitNow: string
|
||||
}
|
||||
}
|
||||
|
||||
export function SubmitProjectCard({ locale, translations }: SubmitProjectCardProps) {
|
||||
const submitProjectTitle = translations?.submitProjectTitle || 'Submit Your Project'
|
||||
const submitProjectDescription = translations?.submitProjectDescription || 'Help us grow the AI ecosystem'
|
||||
const submitNow = translations?.submitNow || 'Submit Now'
|
||||
|
||||
return (
|
||||
<article className="bg-primary border-2 border-black dark:border-gray-600 p-6 shadow-neo hover:shadow-neo-hover hover:-translate-y-1 transition-all duration-200 flex flex-col h-full justify-center items-center text-center group">
|
||||
<div className="bg-white dark:bg-black p-4 rounded-full border-2 border-black mb-4 group-hover:scale-110 transition-transform">
|
||||
<span className="text-3xl dark:text-primary">+</span>
|
||||
</div>
|
||||
<h3 className="font-display text-xl md:text-2xl font-bold text-black mb-2">{t('submitProjectTitle')}</h3>
|
||||
<h3 className="font-display text-xl md:text-2xl font-bold text-black mb-2">{submitProjectTitle}</h3>
|
||||
<p className="text-black mb-6 font-sans text-sm px-4">
|
||||
{t('submitProjectDescription')}
|
||||
{submitProjectDescription}
|
||||
</p>
|
||||
<Link
|
||||
href="#"
|
||||
className="bg-black text-white dark:bg-black dark:text-primary border-2 border-black dark:border-black px-6 py-2 font-display text-sm font-bold uppercase hover:bg-white hover:text-black dark:hover:bg-white dark:hover:text-black transition-colors"
|
||||
>
|
||||
{t('submitNow')}
|
||||
{submitNow}
|
||||
</Link>
|
||||
</article>
|
||||
)
|
||||
|
||||
@@ -21,12 +21,13 @@ interface ProjectListProps {
|
||||
}
|
||||
|
||||
export async function ProjectList({ projects, locale, featured = false }: ProjectListProps) {
|
||||
const t = await getTranslations('common')
|
||||
const tCommon = await getTranslations('common')
|
||||
const tProject = await getTranslations('project')
|
||||
|
||||
if (projects.length === 0) {
|
||||
return (
|
||||
<div className="text-center py-12">
|
||||
<p className="text-gray-500 dark:text-gray-400 font-display">{t('noProjects')}</p>
|
||||
<p className="text-gray-500 dark:text-gray-400 font-display">{tCommon('noProjects')}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -34,12 +35,25 @@ export async function ProjectList({ projects, locale, featured = false }: Projec
|
||||
return (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{projects.map((project) => (
|
||||
<ProjectCard key={project.id} project={project} locale={locale} featured={featured} />
|
||||
<ProjectCard
|
||||
key={project.id}
|
||||
project={project}
|
||||
locale={locale}
|
||||
featured={featured}
|
||||
translations={{ viewDetails: tCommon('viewDetails') }}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* Add "Submit Your Project" CTA card for featured section */}
|
||||
{featured && (
|
||||
<SubmitProjectCard locale={locale} />
|
||||
<SubmitProjectCard
|
||||
locale={locale}
|
||||
translations={{
|
||||
submitProjectTitle: tProject('submitProjectTitle'),
|
||||
submitProjectDescription: tProject('submitProjectDescription'),
|
||||
submitNow: tProject('submitNow'),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -12,9 +12,15 @@ interface AISearchResult {
|
||||
interface AISearchResultsProps {
|
||||
results: AISearchResult[]
|
||||
locale: string
|
||||
translations: {
|
||||
viewDetails: string
|
||||
submitProjectTitle: string
|
||||
submitProjectDescription: string
|
||||
submitNow: string
|
||||
}
|
||||
}
|
||||
|
||||
export function AISearchResults({ results, locale }: AISearchResultsProps) {
|
||||
export function AISearchResults({ results, locale, translations }: AISearchResultsProps) {
|
||||
if (results.length === 0) {
|
||||
return (
|
||||
<div className="text-center py-12 px-4 bg-surface-light dark:bg-surface-dark border-2 border-dashed border-gray-300 dark:border-gray-700 rounded-lg">
|
||||
@@ -51,7 +57,7 @@ export function AISearchResults({ results, locale }: AISearchResultsProps) {
|
||||
|
||||
{/* 项目卡片 */}
|
||||
<div className="ml-3">
|
||||
<ProjectCard project={project} locale={locale} />
|
||||
<ProjectCard project={project} locale={locale} translations={translations} />
|
||||
|
||||
{/* AI 匹配信息 */}
|
||||
<div className="mt-3 px-4 py-3 bg-yellow-50 dark:bg-yellow-900/20 border-l-4 border-yellow-400 dark:border-yellow-500 rounded-r">
|
||||
|
||||
Reference in New Issue
Block a user