feat(admin): analytics redis cache, shared ops health, ticket queue clarity
Co-authored-by: Cursor <[email protected]>
This commit is contained in:
1 parent
474de0717b
commit
58fae1f90f
38 files changed
+348
-245
No files matched your search
@@ -1,4 +1,6 @@
|
||||
import { notFound, redirect } from "next/navigation";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import { TicketQueueBanner } from "@/components/admin/ticket-queue-banner";
|
||||
import { getMinStaffRank } from "@/lib/admin/min-staff-rank";
|
||||
import { positiveBigInt } from "@/lib/api";
|
||||
import { canAccess, getAdminContext, PERMS } from "@/lib/permissions";
|
||||
@@ -17,6 +19,7 @@ export default async function AdminHelpTicketDetailPage({
|
||||
redirect("/admin");
|
||||
}
|
||||
|
||||
const t = await getTranslations("pages.admin.helpTickets");
|
||||
const { id } = await params;
|
||||
const ticketId = positiveBigInt(id);
|
||||
if (!ticketId) notFound();
|
||||
@@ -103,24 +106,31 @@ export default async function AdminHelpTicketDetailPage({
|
||||
const canEdit = canAccess(permissions, PERMS.TICKETS_EDIT, session.user.rank);
|
||||
|
||||
return (
|
||||
<AdminHelpTicketDetail
|
||||
ticket={{
|
||||
id: String(ticket.id),
|
||||
title: ticket.title,
|
||||
open: ticket.open,
|
||||
createdAt: (ticket.createdAt ?? new Date()).toISOString(),
|
||||
updatedAt: (ticket.updatedAt ?? new Date()).toISOString(),
|
||||
creator: creator
|
||||
? {
|
||||
id: creator.id,
|
||||
username: creator.username,
|
||||
rank: creator.rank,
|
||||
mail: creator.mail ?? "",
|
||||
}
|
||||
: null,
|
||||
}}
|
||||
messages={messages}
|
||||
canEdit={canEdit}
|
||||
/>
|
||||
<div className="space-y-6">
|
||||
<TicketQueueBanner
|
||||
hint={t("queueHint")}
|
||||
otherHref="/admin/tickets"
|
||||
otherLabel={t("queueOtherCta")}
|
||||
/>
|
||||
<AdminHelpTicketDetail
|
||||
ticket={{
|
||||
id: String(ticket.id),
|
||||
title: ticket.title,
|
||||
open: ticket.open,
|
||||
createdAt: (ticket.createdAt ?? new Date()).toISOString(),
|
||||
updatedAt: (ticket.updatedAt ?? new Date()).toISOString(),
|
||||
creator: creator
|
||||
? {
|
||||
id: creator.id,
|
||||
username: creator.username,
|
||||
rank: creator.rank,
|
||||
mail: creator.mail ?? "",
|
||||
}
|
||||
: null,
|
||||
}}
|
||||
messages={messages}
|
||||
canEdit={canEdit}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { getTranslations } from "next-intl/server";
|
||||
import { StatusCard } from "@/components/admin/dashboard";
|
||||
import { TicketQueueBanner } from "@/components/admin/ticket-queue-banner";
|
||||
import type { Prisma } from "@/generated/prisma/client";
|
||||
import { fetchTicketQueueOpenCounts } from "@/lib/admin/ticket-queue-counts";
|
||||
import { calcPagination, parseListParams } from "@/lib/admin-helpers";
|
||||
import { formatDate } from "@/lib/format-date";
|
||||
import { canAccess, getAdminContext, PERMS } from "@/lib/permissions";
|
||||
@@ -82,7 +83,7 @@ export default async function AdminHelpTicketsPage({
|
||||
[orderField]: parsed.order,
|
||||
} as Prisma.WebsiteHelpCenterTicketsOrderByWithRelationInput;
|
||||
|
||||
const [total, openCount, closedCount] = await Promise.all([
|
||||
const [total, openCount, closedCount, queues] = await Promise.all([
|
||||
prisma.websiteHelpCenterTickets.count({ where }).catch(() => 0),
|
||||
prisma.websiteHelpCenterTickets
|
||||
.count({ where: { open: true } })
|
||||
@@ -90,6 +91,7 @@ export default async function AdminHelpTicketsPage({
|
||||
prisma.websiteHelpCenterTickets
|
||||
.count({ where: { open: false } })
|
||||
.catch(() => 0),
|
||||
fetchTicketQueueOpenCounts(),
|
||||
]);
|
||||
|
||||
const pagination = calcPagination(total, parsed.page, parsed.perPage);
|
||||
@@ -165,6 +167,10 @@ export default async function AdminHelpTicketsPage({
|
||||
hint={t("queueHint")}
|
||||
otherHref="/admin/tickets"
|
||||
otherLabel={t("queueOtherCta")}
|
||||
countsLine={t("queueCounts", {
|
||||
here: queues.helpOpen,
|
||||
other: queues.cmsOpen,
|
||||
})}
|
||||
/>
|
||||
<div className="grid grid-cols-[repeat(auto-fit,minmax(180px,1fr))] gap-3.5">
|
||||
<StatusCard label={t("statusOpen")} value={openCount} icon="🎫" />
|
||||
|
||||
Reference in new issue
Block a user