124 lines
3.4 KiB
TypeScript
124 lines
3.4 KiB
TypeScript
"use server";
|
|
|
|
import { prisma } from "@/lib/prisma";
|
|
import type { CountRow } from "@/types/dashboard";
|
|
|
|
// Aggregates dashboard stats for admin overview cards and tables.
|
|
function toCountMapSafe<K extends string>(rows: Array<CountRow<K>>, key: K) {
|
|
const out: Record<string, number> = {};
|
|
for (const r of rows) out[String(r[key])] = Number(r?._count?._all ?? 0);
|
|
return out;
|
|
}
|
|
|
|
export async function getAdminDashboard() {
|
|
const now = new Date();
|
|
const days = (n: number) => new Date(now.getTime() - n * 24 * 60 * 60 * 1000);
|
|
|
|
const [
|
|
artworkTotal,
|
|
artworkPublished,
|
|
artworkNeedsWork,
|
|
artworkNsfw,
|
|
artworkHeader,
|
|
colorStatusRows,
|
|
recentArtworks,
|
|
|
|
commissionTotal,
|
|
commissionStatusRows,
|
|
commissionNew7d,
|
|
commissionNew30d,
|
|
recentRequests,
|
|
|
|
userTotal,
|
|
userUnverified,
|
|
userBanned,
|
|
] = await Promise.all([
|
|
prisma.artwork.count(),
|
|
prisma.artwork.count({ where: { published: true } }),
|
|
prisma.artwork.count({ where: { needsWork: true } }),
|
|
prisma.artwork.count({ where: { nsfw: true } }),
|
|
prisma.artwork.count({ where: { setAsHeader: true } }),
|
|
prisma.artwork.groupBy({
|
|
by: ["colorStatus"],
|
|
_count: { _all: true },
|
|
}),
|
|
prisma.artwork.findMany({
|
|
orderBy: { createdAt: "desc" },
|
|
take: 10,
|
|
select: {
|
|
id: true,
|
|
name: true,
|
|
slug: true,
|
|
createdAt: true,
|
|
published: true,
|
|
needsWork: true,
|
|
colorStatus: true,
|
|
},
|
|
}),
|
|
|
|
prisma.commissionRequest.count(),
|
|
prisma.commissionRequest.groupBy({
|
|
by: ["status"],
|
|
_count: { _all: true },
|
|
}),
|
|
prisma.commissionRequest.count({ where: { createdAt: { gte: days(7) } } }),
|
|
prisma.commissionRequest.count({ where: { createdAt: { gte: days(30) } } }),
|
|
prisma.commissionRequest.findMany({
|
|
orderBy: { createdAt: "desc" },
|
|
take: 10,
|
|
select: {
|
|
id: true,
|
|
createdAt: true,
|
|
status: true,
|
|
customerName: true,
|
|
customerEmail: true,
|
|
},
|
|
}),
|
|
|
|
prisma.user.count(),
|
|
prisma.user.count({ where: { emailVerified: false } }),
|
|
prisma.user.count({ where: { banned: true } }),
|
|
]);
|
|
|
|
const colorStatus = toCountMapSafe(colorStatusRows, "colorStatus");
|
|
const commissionStatus = toCountMapSafe(commissionStatusRows, "status");
|
|
|
|
return {
|
|
artworks: {
|
|
total: artworkTotal,
|
|
published: artworkPublished,
|
|
unpublished: artworkTotal - artworkPublished,
|
|
needsWork: artworkNeedsWork,
|
|
nsfw: artworkNsfw,
|
|
header: artworkHeader,
|
|
colorStatus: {
|
|
PENDING: colorStatus.PENDING ?? 0,
|
|
PROCESSING: colorStatus.PROCESSING ?? 0,
|
|
READY: colorStatus.READY ?? 0,
|
|
FAILED: colorStatus.FAILED ?? 0,
|
|
},
|
|
recent: recentArtworks,
|
|
},
|
|
commissions: {
|
|
total: commissionTotal,
|
|
status: {
|
|
NEW: commissionStatus.NEW ?? 0,
|
|
REVIEWING: commissionStatus.REVIEWING ?? 0,
|
|
ACCEPTED: commissionStatus.ACCEPTED ?? 0,
|
|
REJECTED: commissionStatus.REJECTED ?? 0,
|
|
INPROGRESS: commissionStatus.INPROGRESS ?? 0,
|
|
COMPLETED: commissionStatus.COMPLETED ?? 0,
|
|
SPAM: commissionStatus.SPAM ?? 0,
|
|
},
|
|
new7d: commissionNew7d,
|
|
new30d: commissionNew30d,
|
|
recent: recentRequests,
|
|
},
|
|
users: {
|
|
total: userTotal,
|
|
unverified: userUnverified,
|
|
banned: userBanned,
|
|
},
|
|
};
|
|
}
|