- 语言路由:localeDetection 关闭,/ 固定英文、/zh 固定中文,URL 决定语言 (不再按 cookie/Accept-Language 自动跳转;语言切换器显式切换不受影响) - 五站独立视觉语言(data-theme 驱动的设计系统层): · AI Stack:深蓝渐变 Hero + 统计条 + 卡片左色条 hover(专业 SaaS) · LaunchAI:橙色渐变 Banner + 斜切底边 + 暖白底(活力社区) · The AI Ledger:黑色报头日期条 + 衬线字体 + 直角 + 双细线 + 报纸排版(媒体) · DevAI.tools:全站深色 + 等宽字体 + 终端 Hero + 扫描线纹理 + 系统状态条(终端) · findai:纯白极简 + 紫色聚焦光圈 + 无界卡片(搜索引擎) - 修复 :first-of-type 误伤(hero 样式收敛到专用 class)
296 lines
11 KiB
TypeScript
296 lines
11 KiB
TypeScript
// 自动生成:站点首页组件(源自底座 classic 形态,品牌来自 @/site/brand)
|
||
import { siteConfig } from "@/site/brand";
|
||
import { Search, TrendingUp, Sparkles, Clock, Award, LayoutGrid, MessageSquareQuote } from "lucide-react";
|
||
import { getTranslations } from "next-intl/server";
|
||
import { ToolCard, ToolCardGrid } from "@/components/tool-card";
|
||
import { LocaleLink } from "@/components/locale-link";
|
||
import { SectionHeader } from "@/kit/section";
|
||
import { ThemeHeader } from "@/kit/theme-header";
|
||
import { ThemeFooter } from "@/kit/theme-footer";
|
||
import { getHomeDataSafe } from "@/kit/home-data";
|
||
|
||
/** Classic:专业 AI Directory(Clean / Dense / Professional) */
|
||
|
||
|
||
const navItems = [
|
||
{ label: "Discover", href: "/tools" },
|
||
{ label: "Categories", href: "/tools" },
|
||
{ label: "Use Cases", href: "/ai-tools-for" },
|
||
{ label: "Best Lists", href: "/best" },
|
||
{ label: "Rankings", href: "/rankings" },
|
||
{ label: "Blog", href: "/blog" },
|
||
{ label: "Submit", href: "/submit" },
|
||
];
|
||
|
||
const footerGroups = [
|
||
{
|
||
title: "Discover",
|
||
links: [
|
||
{ label: "All Tools", href: "/tools" },
|
||
{ label: "Use Cases", href: "/ai-tools-for" },
|
||
{ label: "Best Lists", href: "/best" },
|
||
{ label: "Rankings", href: "/rankings" },
|
||
],
|
||
},
|
||
{
|
||
title: "Community",
|
||
links: [
|
||
{ label: "Submit a Tool", href: "/submit" },
|
||
{ label: "Blog", href: "/blog" },
|
||
{ label: "Dashboard", href: "/dashboard" },
|
||
],
|
||
},
|
||
{
|
||
title: "Legal",
|
||
links: [
|
||
{ label: "Terms", href: "/terms" },
|
||
{ label: "Privacy", href: "/privacy" },
|
||
{ label: "Contact", href: "/contact" },
|
||
],
|
||
},
|
||
];
|
||
|
||
async function SiteHome({ locale }: { locale: string }) {
|
||
const t = await getTranslations("siteHome");
|
||
const th = await getTranslations("hero");
|
||
const data = await getHomeDataSafe(8);
|
||
|
||
return (
|
||
<div className="bg-background">
|
||
{/* Hero */}
|
||
<section className="hero-dark border-b border-border">
|
||
<div className="mx-auto max-w-7xl px-4 py-16 text-center sm:px-6 md:py-24">
|
||
<h1 className="mx-auto max-w-3xl text-3xl font-bold tracking-tight text-foreground sm:text-4xl md:text-5xl">
|
||
{th("title")}
|
||
</h1>
|
||
<p className="mx-auto mt-4 max-w-2xl text-sm text-muted-foreground sm:text-base">
|
||
{th("description")}
|
||
</p>
|
||
<form action={`/${locale}/tools`} className="mx-auto mt-8 max-w-xl">
|
||
<div className="relative">
|
||
<Search className="absolute left-4 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
|
||
<input
|
||
name="search"
|
||
placeholder={t("searchPlaceholder")}
|
||
className="h-12 w-full rounded-full border border-border bg-background pl-11 pr-28 text-sm text-foreground shadow-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/30"
|
||
aria-label="Search AI tools"
|
||
/>
|
||
<button
|
||
type="submit"
|
||
className="absolute right-1.5 top-1.5 h-9 rounded-full bg-primary px-5 text-xs font-semibold text-primary-foreground transition-opacity hover:opacity-90"
|
||
>
|
||
{t("searchButton")}
|
||
</button>
|
||
</div>
|
||
</form>
|
||
|
||
{/* 统计条(深色 Hero 内的白色数字) */}
|
||
<div className="mx-auto mt-10 flex max-w-lg items-center justify-center gap-10 border-t border-white/15 pt-6">
|
||
{[
|
||
{ v: "1,000+", l: "AI tools" },
|
||
{ v: "14", l: "Categories" },
|
||
{ v: "7", l: "Rankings" },
|
||
].map((s) => (
|
||
<div key={s.l} className="text-center">
|
||
<div className="text-2xl font-bold text-white">{s.v}</div>
|
||
<div className="text-xs text-white/60">{s.l}</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
<div className="mt-8 flex flex-wrap items-center justify-center gap-2 text-xs">
|
||
<span className="text-white/60">{t("popularLabel")}</span>
|
||
{t.raw("popularSearches").map((q: string) => (
|
||
<LocaleLink
|
||
key={q}
|
||
href={`/tools?search=${q}`}
|
||
className="rounded-full bg-white/10 px-2.5 py-1 text-white/80 transition-colors hover:bg-white/20"
|
||
>
|
||
{q}
|
||
</LocaleLink>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<div className="mx-auto max-w-7xl space-y-12 px-4 py-12 sm:px-6">
|
||
{/* Trending */}
|
||
{data.trending.length > 0 && (
|
||
<section>
|
||
<SectionHeader
|
||
title={t("trendingTitle")}
|
||
subtitle={t("trendingSubtitle")}
|
||
viewAllHref="/rankings?board=trending"
|
||
viewAllLabel={t("allRankings")}
|
||
/>
|
||
<ToolCardGrid tools={data.trending} locale={locale} ranked />
|
||
</section>
|
||
)}
|
||
|
||
{/* Categories */}
|
||
{data.categories.length > 0 && (
|
||
<section>
|
||
<SectionHeader
|
||
title={t("categoriesTitle")}
|
||
viewAllHref="/tools"
|
||
viewAllLabel={t("categoriesViewAll")}
|
||
/>
|
||
<div className="grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-5">
|
||
{data.categories.slice(0, 10).map((category) => (
|
||
<LocaleLink
|
||
key={category.id}
|
||
href={`/category/${category.slug}`}
|
||
className="group rounded-xl border border-border p-4 transition-all hover:border-primary/30 hover:shadow-sm"
|
||
>
|
||
<LayoutGrid className="h-4 w-4 text-primary" />
|
||
<h3 className="mt-2 truncate text-sm font-medium text-foreground group-hover:text-primary">
|
||
{locale === "zh" && category.nameZh
|
||
? category.nameZh
|
||
: category.nameEn}
|
||
</h3>
|
||
<p className="mt-0.5 text-xs text-muted-foreground tabular-nums">
|
||
{category.toolCount} tools
|
||
</p>
|
||
</LocaleLink>
|
||
))}
|
||
</div>
|
||
</section>
|
||
)}
|
||
|
||
{/* New + Editor's Choice 两列 */}
|
||
<div className="grid gap-12 lg:grid-cols-2">
|
||
{data.newest.length > 0 && (
|
||
<section>
|
||
<SectionHeader
|
||
title={t("newTitle")}
|
||
viewAllHref="/rankings?board=new"
|
||
/>
|
||
<ToolCardGrid tools={data.newest.slice(0, 4)} locale={locale} />
|
||
</section>
|
||
)}
|
||
{data.editorsChoice.length > 0 && (
|
||
<section>
|
||
<SectionHeader
|
||
title={t("editorsTitle")}
|
||
viewAllHref="/rankings?board=editors-choice"
|
||
/>
|
||
<ToolCardGrid tools={data.editorsChoice.slice(0, 4)} locale={locale} />
|
||
</section>
|
||
)}
|
||
</div>
|
||
|
||
{/* Use Cases */}
|
||
{data.useCases.length > 0 && (
|
||
<section>
|
||
<SectionHeader
|
||
title={t("useCasesTitle")}
|
||
subtitle={t("useCasesSubtitle")}
|
||
viewAllHref="/ai-tools-for"
|
||
viewAllLabel={t("useCasesViewAll")}
|
||
/>
|
||
<div className="flex flex-wrap gap-2">
|
||
{data.useCases.slice(0, 12).map((uc) => (
|
||
<LocaleLink
|
||
key={uc.id}
|
||
href={`/ai-tools-for/${uc.slug}`}
|
||
className="rounded-full border border-border px-3.5 py-1.5 text-xs text-foreground transition-colors hover:border-primary/40 hover:bg-primary/5"
|
||
>
|
||
{locale === "zh" && uc.nameZh ? uc.nameZh : uc.nameEn}
|
||
<span className="ml-1.5 text-muted-foreground tabular-nums">
|
||
{uc.toolCount}
|
||
</span>
|
||
</LocaleLink>
|
||
))}
|
||
</div>
|
||
</section>
|
||
)}
|
||
|
||
{/* Best Lists */}
|
||
{data.collections.length > 0 && (
|
||
<section>
|
||
<SectionHeader
|
||
title={t("bestListsTitle")}
|
||
subtitle={t("bestListsSubtitle")}
|
||
viewAllHref="/best"
|
||
viewAllLabel={t("bestListsViewAll")}
|
||
/>
|
||
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
|
||
{data.collections.slice(0, 3).map((col) => (
|
||
<LocaleLink
|
||
key={col.id}
|
||
href={`/best/${col.slug}`}
|
||
className="group rounded-xl border border-border p-5 transition-all hover:border-primary/30 hover:shadow-sm"
|
||
>
|
||
<Award className="h-4 w-4 text-primary" />
|
||
<h3 className="mt-2 text-sm font-semibold text-foreground group-hover:text-primary">
|
||
{locale === "zh" && col.nameZh ? col.nameZh : col.nameEn}
|
||
</h3>
|
||
<p className="mt-1 line-clamp-2 text-xs text-muted-foreground">
|
||
{locale === "zh" && col.introZh ? col.introZh : col.introEn}
|
||
</p>
|
||
<p className="mt-2 text-xs text-muted-foreground tabular-nums">
|
||
{col.toolCount} tools
|
||
</p>
|
||
</LocaleLink>
|
||
))}
|
||
</div>
|
||
</section>
|
||
)}
|
||
|
||
{/* Latest Reviews */}
|
||
{data.latestReviews.length > 0 && (
|
||
<section>
|
||
<SectionHeader title={t("reviewsTitle")} />
|
||
<div className="grid gap-3 md:grid-cols-2">
|
||
{data.latestReviews.map((review, i) => (
|
||
<LocaleLink
|
||
key={i}
|
||
href={`/tools/${review.toolSlug}#reviews`}
|
||
className="rounded-xl border border-border p-4 transition-colors hover:border-primary/30"
|
||
>
|
||
<div className="flex items-center gap-2">
|
||
<MessageSquareQuote className="h-3.5 w-3.5 text-primary" />
|
||
<span className="text-xs font-medium text-foreground">
|
||
{review.authorName}
|
||
</span>
|
||
<span className="text-xs text-muted-foreground">
|
||
on {review.toolName}
|
||
</span>
|
||
<span className="ml-auto text-xs font-semibold text-amber-500 tabular-nums">
|
||
{review.rating}/5
|
||
</span>
|
||
</div>
|
||
<p className="mt-2 line-clamp-2 text-xs leading-relaxed text-muted-foreground">
|
||
{review.content}
|
||
</p>
|
||
</LocaleLink>
|
||
))}
|
||
</div>
|
||
</section>
|
||
)}
|
||
|
||
{/* Submit CTA */}
|
||
<section className="rounded-2xl border border-border bg-secondary/30 p-8 text-center">
|
||
<Sparkles className="mx-auto h-5 w-5 text-primary" />
|
||
<h2 className="mt-3 text-lg font-semibold text-foreground">
|
||
Built an AI tool?
|
||
</h2>
|
||
<p className="mx-auto mt-1 max-w-md text-sm text-muted-foreground">
|
||
Submit it to {siteConfig.brand.name} and get discovered by thousands of users
|
||
looking for tools like yours.
|
||
</p>
|
||
<LocaleLink
|
||
href="/submit"
|
||
className="mt-4 inline-flex items-center gap-1.5 rounded-full bg-primary px-5 py-2 text-sm font-semibold text-primary-foreground transition-opacity hover:opacity-90"
|
||
>
|
||
<TrendingUp className="h-4 w-4" />
|
||
Submit your tool
|
||
</LocaleLink>
|
||
</section>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export { SiteHome };
|