Files
EpicNext-Cms/src/app/admin/catalog/page.tsx
T
2026-07-11 20:37:56 +02:00

131 lines
4.4 KiB
TypeScript

import Link from "next/link";
import { getTranslations } from "next-intl/server";
import { StatusCard } from "@/components/admin/dashboard";
import { prisma } from "@/lib/prisma";
import { Package } from "lucide-react";
export const dynamic = "force-dynamic";
type CatalogPageRow = {
id: number;
caption: string;
parentId: number;
pageLayout: string;
minRank: number;
orderNum: number;
visible: string;
enabled: string;
};
export default async function AdminCatalog() {
const t = await getTranslations("pages.admin.catalog");
let pages: CatalogPageRow[] = [];
try {
pages = await prisma.catalogPages.findMany({
select: {
id: true,
caption: true,
parentId: true,
pageLayout: true,
minRank: true,
orderNum: true,
visible: true,
enabled: true,
},
orderBy: [{ parentId: "asc" }, { orderNum: "asc" }, { id: "asc" }],
take: 500,
});
} catch {
pages = [];
}
const captionById = new Map<number, string>();
for (const p of pages) captionById.set(p.id, p.caption);
const visibleCount = pages.filter(
(p) => p.visible === "1" && p.enabled === "1",
).length;
const hiddenCount = pages.length - visibleCount;
const rootCount = pages.filter((p) => p.parentId <= 0).length;
return (
<main>
<div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center">
<Package size={20} className="text-[var(--color-primary)]" />
</div>
<div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("subtitle")}</p>
</div>
</div>
<div className="grid grid-cols-[repeat(auto-fit,minmax(180px,1fr))] gap-3.5 mb-6">
<StatusCard label={t("pages")} value={pages.length} icon="📚" />
<StatusCard label={t("visible")} value={visibleCount} state="ok" icon="👁️" />
<StatusCard
label={t("hidden")}
value={hiddenCount}
state={hiddenCount > 0 ? "warn" : "neutral"}
icon="🚫"
/>
<StatusCard label={t("rootPages")} value={rootCount} icon="🗂️" />
</div>
<section className="mt-6">
<h2>{t("pages")} ({pages.length})</h2>
{pages.length === 0 ? (
<p className="muted">{t("noPages")}</p>
) : (
<div style={{ overflowX: "auto" }}>
<table>
<thead>
<tr>
<th>{t("colId")}</th>
<th>{t("caption")}</th>
<th>{t("colParent")}</th>
<th>{t("colLayout")}</th>
<th>{t("colMinRank")}</th>
<th>{t("colOrder")}</th>
<th>{t("colStatus")}</th>
</tr>
</thead>
<tbody>
{pages.map((p) => {
const parentName =
p.parentId > 0 ? captionById.get(p.parentId) : null;
const hidden = p.visible !== "1" || p.enabled !== "1";
return (
<tr key={p.id}>
<td className="muted">{p.id}</td>
<td>
<Link href={`/admin/catalog/${p.id}`}>{p.caption}</Link>
</td>
<td className="muted">
{p.parentId <= 0
? "—"
: parentName
? `${parentName} (#${p.parentId})`
: `#${p.parentId}`}
</td>
<td className="muted">{p.pageLayout}</td>
<td>{p.minRank}</td>
<td>{p.orderNum}</td>
<td>
<span className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${hidden ? "bg-red-500/22 text-[var(--color-text)]" : "bg-green-600/20 text-[var(--color-text)]"}`}>
{hidden ? t("statusHidden") : t("statusVisible")}
</span>
</td>
</tr>
);
})}
</tbody>
</table>
</div>
)}
</section>
</main>
);
}