Files
nanami-web/src/app/(public)/addons/page.tsx

184 lines
6.4 KiB
TypeScript

import { prisma } from "@/lib/db";
import { AddonCard } from "@/components/public/AddonCard";
import Link from "next/link";
import { Package, Search, ChevronLeft, ChevronRight } from "lucide-react";
const categoryLabels: Record<string, string> = {
general: "通用",
gameplay: "游戏玩法",
ui: "界面增强",
combat: "战斗",
raid: "团队副本",
pvp: "PvP",
tradeskill: "专业技能",
utility: "实用工具",
};
export const metadata = {
title: "插件列表 - Nanami",
description: "浏览和下载 Turtle WoW 插件",
};
export const revalidate = 30;
const PAGE_SIZE = 12;
export default async function AddonsPage({
searchParams,
}: {
searchParams: Promise<{ category?: string; search?: string; page?: string }>;
}) {
const { category, search, page: pageStr } = await searchParams;
const page = Math.max(1, parseInt(pageStr || "1", 10) || 1);
const where: Record<string, unknown> = { published: true };
if (category) where.category = category;
if (search) {
where.OR = [
{ name: { contains: search, mode: "insensitive" } },
{ summary: { contains: search, mode: "insensitive" } },
];
}
const [addons, total, categories] = await Promise.all([
prisma.addon.findMany({
where,
include: {
releases: {
where: { isLatest: true },
select: { version: true },
},
},
orderBy: { totalDownloads: "desc" },
skip: (page - 1) * PAGE_SIZE,
take: PAGE_SIZE,
}),
prisma.addon.count({ where }),
prisma.addon.groupBy({
by: ["category"],
where: { published: true },
_count: { id: true },
}),
]);
const totalPages = Math.ceil(total / PAGE_SIZE);
const buildHref = (p: number) => {
const params = new URLSearchParams();
if (category) params.set("category", category);
if (search) params.set("search", search);
if (p > 1) params.set("page", String(p));
const qs = params.toString();
return `/addons${qs ? `?${qs}` : ""}`;
};
return (
<section className="mx-auto max-w-6xl px-3 py-10 sm:px-4 sm:py-16">
<div className="mb-2 flex items-center gap-2 sm:gap-3">
<Package className="h-5 w-5 text-amber-400 sm:h-6 sm:w-6" />
<h1 className="text-2xl font-bold text-amber-100 sm:text-3xl">
</h1>
</div>
<p className="mb-6 text-sm text-gray-400 sm:mb-10 sm:text-base">
World of Warcraft
</p>
{/* Category Filter */}
<div className="mb-6 flex flex-wrap gap-2 sm:mb-8">
<Link
href="/addons"
className={`rounded-lg px-3 py-1.5 text-xs font-medium transition-all sm:text-sm ${
!category
? "bg-amber-500/20 text-amber-200 ring-1 ring-amber-500/30"
: "bg-white/[0.04] text-gray-400 hover:bg-white/[0.08] hover:text-gray-300"
}`}
>
</Link>
{categories.map((cat) => (
<Link
key={cat.category}
href={`/addons?category=${cat.category}`}
className={`rounded-lg px-3 py-1.5 text-xs font-medium transition-all sm:text-sm ${
category === cat.category
? "bg-amber-500/20 text-amber-200 ring-1 ring-amber-500/30"
: "bg-white/[0.04] text-gray-400 hover:bg-white/[0.08] hover:text-gray-300"
}`}
>
{categoryLabels[cat.category] || cat.category} ({cat._count.id})
</Link>
))}
</div>
{/* Addon Grid */}
{addons.length > 0 ? (
<>
<div className="grid gap-4 sm:gap-6 sm:grid-cols-2 lg:grid-cols-3">
{addons.map((addon) => (
<AddonCard key={addon.id} addon={addon} />
))}
</div>
{/* Pagination */}
{totalPages > 1 && (
<div className="mt-10 flex items-center justify-center gap-2">
{page > 1 ? (
<Link
href={buildHref(page - 1)}
className="flex h-9 w-9 items-center justify-center rounded-lg border border-amber-500/15 bg-white/[0.03] text-gray-400 transition-colors hover:border-amber-500/30 hover:text-amber-200"
>
<ChevronLeft className="h-4 w-4" />
</Link>
) : (
<span className="flex h-9 w-9 items-center justify-center rounded-lg border border-white/5 text-gray-600">
<ChevronLeft className="h-4 w-4" />
</span>
)}
{Array.from({ length: totalPages }, (_, i) => i + 1).map((p) => (
<Link
key={p}
href={buildHref(p)}
className={`flex h-9 w-9 items-center justify-center rounded-lg text-sm font-medium transition-colors ${
p === page
? "bg-amber-500/20 text-amber-200 ring-1 ring-amber-500/30"
: "border border-amber-500/10 bg-white/[0.03] text-gray-400 hover:border-amber-500/25 hover:text-amber-200"
}`}
>
{p}
</Link>
))}
{page < totalPages ? (
<Link
href={buildHref(page + 1)}
className="flex h-9 w-9 items-center justify-center rounded-lg border border-amber-500/15 bg-white/[0.03] text-gray-400 transition-colors hover:border-amber-500/30 hover:text-amber-200"
>
<ChevronRight className="h-4 w-4" />
</Link>
) : (
<span className="flex h-9 w-9 items-center justify-center rounded-lg border border-white/5 text-gray-600">
<ChevronRight className="h-4 w-4" />
</span>
)}
</div>
)}
</>
) : (
<div className="flex flex-col items-center py-20">
<div className="mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-amber-500/10">
<Search className="h-7 w-7 text-amber-400/60" />
</div>
<p className="text-lg font-medium text-gray-400">
{search ? `没有找到"${search}"相关的插件` : "暂无插件"}
</p>
<p className="mt-1 text-sm text-gray-500">
{search ? "尝试更换关键词搜索" : "稍后再来查看吧"}
</p>
</div>
)}
</section>
);
}