feat(admin): P1 — clearer tickets labels, pagination, min_staff_rank, analytics errors
Co-authored-by: Cursor <[email protected]>
This commit is contained in:
1 parent
75cdfdebe4
commit
f150aea8b9
13 files changed
+311
-101
No files matched your search
@@ -1,14 +1,17 @@
|
||||
import Link from "next/link";
|
||||
import { redirect } from "next/navigation";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import { AdminSimplePager } from "@/components/admin/admin-simple-pager";
|
||||
import { StatusCard } from "@/components/admin/dashboard";
|
||||
import { CurrencyIcon } from "@/components/shared/currency-icon";
|
||||
import { calcPagination, parseListParams } from "@/lib/admin-helpers";
|
||||
import { canAccess, getAdminContext, PERMS } from "@/lib/permissions";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
const PAID_STATUS = new Set(["completed", "paid", "success", "approved"]);
|
||||
const RECENT_ORDERS = 25;
|
||||
|
||||
function statusState(
|
||||
status: string | null | undefined,
|
||||
@@ -45,36 +48,60 @@ function CurrencyCell({
|
||||
);
|
||||
}
|
||||
|
||||
export default async function AdminShop() {
|
||||
export default async function AdminShop({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: Promise<Record<string, string>>;
|
||||
}) {
|
||||
const { session, permissions } = await getAdminContext();
|
||||
if (!canAccess(permissions, PERMS.SHOP_VIEW, session.user.rank)) {
|
||||
redirect("/admin");
|
||||
}
|
||||
|
||||
const t = await getTranslations("pages.admin.shop");
|
||||
const raw = await searchParams;
|
||||
const parsed = parseListParams(new URLSearchParams(raw));
|
||||
|
||||
let packageTotal = 0;
|
||||
let packages: Awaited<
|
||||
ReturnType<typeof prisma.websiteShopArticles.findMany>
|
||||
> = [];
|
||||
let loadError = false;
|
||||
try {
|
||||
packageTotal = await prisma.websiteShopArticles.count();
|
||||
const pagination = calcPagination(
|
||||
packageTotal,
|
||||
parsed.page,
|
||||
parsed.perPage,
|
||||
);
|
||||
packages = await prisma.websiteShopArticles.findMany({
|
||||
orderBy: [{ position: "asc" }, { name: "asc" }],
|
||||
take: 500,
|
||||
skip: pagination.offset,
|
||||
take: pagination.perPage,
|
||||
});
|
||||
} catch {
|
||||
packages = [];
|
||||
packageTotal = 0;
|
||||
loadError = true;
|
||||
}
|
||||
|
||||
const pkgPagination = calcPagination(
|
||||
packageTotal,
|
||||
parsed.page,
|
||||
parsed.perPage,
|
||||
);
|
||||
|
||||
let orders: Awaited<
|
||||
ReturnType<typeof prisma.websitePaypalTransactions.findMany>
|
||||
> = [];
|
||||
try {
|
||||
orders = await prisma.websitePaypalTransactions.findMany({
|
||||
orderBy: { createdAt: "desc" },
|
||||
take: 50,
|
||||
take: RECENT_ORDERS,
|
||||
});
|
||||
} catch {
|
||||
orders = [];
|
||||
loadError = true;
|
||||
}
|
||||
|
||||
const paidOrders = orders.filter(
|
||||
@@ -83,6 +110,11 @@ export default async function AdminShop() {
|
||||
|
||||
return (
|
||||
<main>
|
||||
{loadError ? (
|
||||
<div className="mb-4 rounded-lg bg-[var(--admin-error-subtle)] p-3 text-xs text-[var(--admin-error)]">
|
||||
Could not load some shop data (database unavailable).
|
||||
</div>
|
||||
) : null}
|
||||
<div className="flex items-center justify-end gap-4">
|
||||
<Link href="/admin/shop/new" className="btn btn-primary">
|
||||
{t("newPackage")}
|
||||
@@ -90,7 +122,7 @@ export default async function AdminShop() {
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-[repeat(auto-fit,minmax(180px,1fr))] gap-3.5 mb-6">
|
||||
<StatusCard label={t("packages")} value={packages.length} icon="🛍️" />
|
||||
<StatusCard label={t("packages")} value={packageTotal} icon="🛍️" />
|
||||
<StatusCard label={t("recentOrders")} value={orders.length} icon="🧾" />
|
||||
<StatusCard
|
||||
label={t("paidOrders")}
|
||||
@@ -102,7 +134,7 @@ export default async function AdminShop() {
|
||||
|
||||
<section className="mt-6">
|
||||
<h2 className="admin-section-title">
|
||||
{t("packages")} ({packages.length})
|
||||
{t("packagesTotal", { count: packageTotal })}
|
||||
</h2>
|
||||
{packages.length === 0 ? (
|
||||
<div className="admin-empty">{t("noPackages")}</div>
|
||||
@@ -146,6 +178,12 @@ export default async function AdminShop() {
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
<AdminSimplePager
|
||||
page={pkgPagination.page}
|
||||
lastPage={pkgPagination.lastPage}
|
||||
total={packageTotal}
|
||||
basePath="/admin/shop"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section className="mt-6">
|
||||
|
||||
Reference in new issue
Block a user