Your Name f6b7758ec9
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled
feat: extend lovart rebuild with blog and changelog experiences
2026-02-12 12:11:47 +08:00

153 lines
6.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Link from "next/link";
import { Calendar, WandSparkles } from "lucide-react";
import { MarketingShell } from "@/components/lovart/marketing-shell";
import { Reveal } from "@/components/lovart/reveal";
import {
getNavLinks,
getPublicChangelogEvents,
getPublicFeaturedRanking,
getSiteSettings,
} from "@/lib/db/queries";
function formatDate(value: Date | null | undefined) {
if (!value) {
return "最近更新";
}
return new Intl.DateTimeFormat("zh-CN", {
year: "numeric",
month: "2-digit",
day: "2-digit",
}).format(new Date(value));
}
export default async function LovartChangelogPage() {
const [settings, navLinks, events, ranking] = await Promise.all([
getSiteSettings(),
getNavLinks(),
getPublicChangelogEvents(18),
getPublicFeaturedRanking(6),
]);
const tutorialCount = events.filter((event) => event.kind === "tutorial").length;
const featuredCount = events.filter((event) => event.kind === "featured").length;
return (
<MarketingShell
active="changelog"
bannerText={settings.bannerEnabled ? settings.bannerText : null}
navLinks={navLinks}
siteLogo={settings.siteLogo || settings.siteLogoDark}
siteName={settings.siteName || "AI Chatbot"}
stats={[
{ label: "更新事件", value: String(events.length) },
{ label: "教程更新", value: String(tutorialCount) },
{ label: "精选更新", value: String(featuredCount) },
]}
>
<Reveal>
<section className="rounded-3xl border border-white/15 bg-white/[0.04] p-6 sm:p-10">
<p className="inline-flex items-center gap-2 rounded-full border border-white/20 bg-white/5 px-3 py-1 text-xs text-zinc-200">
<WandSparkles className="h-3.5 w-3.5 text-cyan-300" />
Product Changelog
</p>
<h1 className="mt-4 text-3xl font-semibold text-white sm:text-5xl">更新日志</h1>
<p className="mt-4 max-w-2xl text-sm text-zinc-300 sm:text-base">
自动聚合教程发布与精选作品上新,形成可追踪的产品更新时间线。
</p>
</section>
</Reveal>
<Reveal delay={0.07}>
<section className="mt-8 grid gap-4 lg:grid-cols-[1.25fr_1fr]">
<article className="rounded-2xl border border-white/15 bg-black/30 p-5">
<h2 className="mb-4 text-lg font-semibold text-white">近期更新时间线</h2>
{events.length > 0 ? (
<div className="space-y-3">
{events.map((event) => (
<div
key={event.id}
className="rounded-xl border border-white/10 bg-white/[0.03] p-3"
>
<div className="mb-1 flex items-center justify-between text-[11px] text-zinc-400">
<span className="inline-flex items-center gap-1">
<Calendar className="h-3 w-3" />
{formatDate(event.occurredAt)}
</span>
<span className="rounded-full border border-white/15 px-2 py-0.5 text-[10px] uppercase tracking-wide text-zinc-300">
{event.kind}
</span>
</div>
<p className="line-clamp-2 text-sm font-medium text-zinc-100">{event.title}</p>
<p className="mt-1 text-xs text-zinc-300">{event.summary}</p>
<div className="mt-2 flex items-center justify-between gap-2">
<span className="line-clamp-1 text-[11px] text-zinc-400">{event.detail}</span>
{event.href ? (
<a
href={event.href}
target="_blank"
rel="noreferrer"
className="text-[11px] text-cyan-300 hover:text-cyan-200"
>
查看
</a>
) : null}
</div>
</div>
))}
</div>
) : (
<p className="text-sm text-zinc-300">暂无更新记录</p>
)}
</article>
<article className="rounded-2xl border border-white/15 bg-black/30 p-5">
<h2 className="mb-3 text-lg font-semibold text-white">精选作者榜</h2>
<div className="space-y-2">
{ranking.length > 0 ? (
ranking.map((item, index) => (
<div
key={item.userId}
className="flex items-center justify-between rounded-lg border border-white/10 bg-white/[0.03] px-3 py-2 text-sm"
>
<span className="text-zinc-200">
{index + 1}. {item.userName || item.userEmail?.split("@")[0] || "未知用户"}
</span>
<span className="font-medium text-amber-200">{item.featuredCount}</span>
</div>
))
) : (
<p className="text-sm text-zinc-300">暂无榜单数据</p>
)}
</div>
<div className="mt-4 rounded-xl border border-white/10 bg-white/[0.03] p-3 text-xs text-zinc-300">
<p className="font-medium text-zinc-100">说明</p>
<p className="mt-1">
该榜单来自精选图库实时统计,可用于运营激励展示。
</p>
</div>
</article>
</section>
</Reveal>
<Reveal delay={0.14}>
<section className="mt-8 rounded-2xl border border-white/15 bg-black/25 p-5">
<h2 className="text-lg font-semibold text-white">下一步</h2>
<div className="mt-3 flex flex-wrap gap-3 text-sm text-zinc-300">
<Link className="rounded-lg border border-white/20 px-3 py-1.5 hover:border-white/40" href="/lovart/pricing">
继续接支付与订阅
</Link>
<Link className="rounded-lg border border-white/20 px-3 py-1.5 hover:border-white/40" href="/lovart/news">
查看新闻中心
</Link>
<Link className="rounded-lg border border-white/20 px-3 py-1.5 hover:border-white/40" href="/admin/settings">
去后台运营配置
</Link>
</div>
</section>
</Reveal>
</MarketingShell>
);
}