153 lines
6.3 KiB
TypeScript
153 lines
6.3 KiB
TypeScript
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>
|
||
);
|
||
}
|