import { ArrowLeftRight, Coins, TrendingDown, TrendingUp } from "lucide-react"; import dynamic from "next/dynamic"; import { redirect } from "next/navigation"; import { StatsCard } from "@/components/admin/stats-card"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Skeleton } from "@/components/ui/skeleton"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; const RevenueChart = dynamic( () => import("@/components/admin/revenue-chart").then((m) => ({ default: m.RevenueChart, })), { loading: () => }, ); async function getEconomyData() { const now = Math.floor(Date.now() / 1000); const todayStart = now - (now % 86400); const weekAgo = now - 7 * 86400; // Total currency in circulation — parallel instead of sequential const [creditsAgg, pixelsAgg, pointsAgg] = await Promise.all([ prisma.user.aggregate({ _sum: { credits: true } }), prisma.user.aggregate({ _sum: { pixels: true } }), prisma.user.aggregate({ _sum: { points: true } }), ]); const totalCredits = creditsAgg._sum.credits ?? 0; const totalPixels = pixelsAgg._sum.pixels ?? 0; const totalPoints = pointsAgg._sum.points ?? 0; // Shop revenue (7 days) const shopRevenue = await prisma.logsShopPurchases.aggregate({ where: { timestamp: { gte: weekAgo } }, _sum: { costCredits: true, costPoints: true }, _count: true, }); // Marketplace stats const marketplaceActive = await prisma.marketplaceItems.count({ where: { state: 1 }, }); const marketplaceSold = await prisma.marketplaceItems.count({ where: { state: 2, soldTimestamp: { gte: weekAgo } }, }); // Revenue chart (7 days) — single GROUP BY query instead of 7 sequential queries const rangeStart = todayStart - 6 * 86400; const revRows = await prisma.$queryRaw< { day_bucket: bigint; credits: number; points: number }[] >` SELECT FLOOR(timestamp / 86400) AS day_bucket, COALESCE(SUM(cost_credits), 0) AS credits, COALESCE(SUM(cost_points), 0) AS points FROM logs_shop_purchases WHERE timestamp >= ${rangeStart} GROUP BY day_bucket ORDER BY day_bucket `; const revMap = new Map( revRows.map((r) => [ Number(r.day_bucket), { credits: Number(r.credits), points: Number(r.points) }, ]), ); const revenueData = []; for (let i = 6; i >= 0; i--) { const dayStart = todayStart - i * 86400; const bucket = Math.floor(dayStart / 86400); const date = new Date(dayStart * 1000); const entry = revMap.get(bucket); revenueData.push({ label: date.toLocaleDateString("en", { weekday: "short" }), credits: entry?.credits ?? 0, points: entry?.points ?? 0, }); } // Trades (7 days) const weekTrades = await prisma.roomTradeLog.count({ where: { timestamp: { gte: weekAgo } }, }); return { totalCredits, totalPixels, totalPoints, shopPurchases: typeof shopRevenue._count === "number" ? shopRevenue._count : 0, shopCreditsSpent: shopRevenue._sum?.costCredits ?? 0, shopPointsSpent: shopRevenue._sum?.costPoints ?? 0, marketplaceActive, marketplaceSold, weekTrades, revenueData, }; } export default async function EconomyPage() { const { session, permissions } = await getAdminContext(); if (!canAccess(permissions, PERMS.ANALYTICS_VIEW, session.user.rank)) { redirect("/admin"); } const data = await getEconomyData(); return (

Economy

Currency circulation, shop revenue and marketplace activity

} /> } /> } /> } />
Shop Revenue (7d)
Purchases {data.shopPurchases}
Credits spent {data.shopCreditsSpent.toLocaleString()}
Points spent {data.shopPointsSpent.toLocaleString()}
Marketplace
Active listings {data.marketplaceActive}
Sold (7d) {data.marketplaceSold}
Currency Distribution
Credits {data.totalCredits.toLocaleString()}
Pixels {data.totalPixels.toLocaleString()}
Points {data.totalPoints.toLocaleString()}
); }