mirror of
https://github.com/System-End/scraps.git
synced 2026-04-19 20:55:12 +00:00
add in more admin info
This commit is contained in:
parent
68043e888a
commit
9289d0bbe9
2 changed files with 101 additions and 1 deletions
|
|
@ -16,7 +16,7 @@ import {
|
|||
import { newsTable } from "../schemas/news";
|
||||
import { projectActivityTable } from "../schemas/activity";
|
||||
import { getUserFromSession } from "../lib/auth";
|
||||
import { calculateScrapsFromHours, getUserScrapsBalance, TIER_MULTIPLIERS, DOLLARS_PER_HOUR } from "../lib/scraps";
|
||||
import { calculateScrapsFromHours, getUserScrapsBalance, TIER_MULTIPLIERS, DOLLARS_PER_HOUR, SCRAPS_PER_DOLLAR } from "../lib/scraps";
|
||||
import { payoutPendingScraps, getNextPayoutDate } from "../lib/scraps-payout";
|
||||
import { syncSingleProject } from "../lib/hackatime-sync";
|
||||
import { computeItemPricing, updateShopItemPricing } from "../lib/shop-pricing";
|
||||
|
|
@ -183,6 +183,31 @@ admin.get("/stats", async ({ headers, status }) => {
|
|||
? Math.round((totalTierCost / roundedTotalHours) * 100) / 100
|
||||
: 0;
|
||||
|
||||
// Real dollar cost from shop fulfillment
|
||||
const [luckWinOrders, consolationCount] = await Promise.all([
|
||||
db
|
||||
.select({
|
||||
itemPrice: shopItemsTable.price,
|
||||
})
|
||||
.from(shopOrdersTable)
|
||||
.innerJoin(shopItemsTable, eq(shopOrdersTable.shopItemId, shopItemsTable.id))
|
||||
.where(eq(shopOrdersTable.orderType, "luck_win")),
|
||||
db
|
||||
.select({ count: sql<number>`count(*)` })
|
||||
.from(shopOrdersTable)
|
||||
.where(eq(shopOrdersTable.orderType, "consolation")),
|
||||
]);
|
||||
|
||||
const luckWinDollarCost = luckWinOrders.reduce(
|
||||
(sum, o) => sum + o.itemPrice / SCRAPS_PER_DOLLAR,
|
||||
0,
|
||||
);
|
||||
const consolationDollarCost = Number(consolationCount[0]?.count || 0) * 2;
|
||||
const totalRealCost = luckWinDollarCost + consolationDollarCost;
|
||||
const realCostPerHour = roundedTotalHours > 0
|
||||
? Math.round((totalRealCost / roundedTotalHours) * 100) / 100
|
||||
: 0;
|
||||
|
||||
return {
|
||||
totalUsers,
|
||||
totalProjects,
|
||||
|
|
@ -203,6 +228,14 @@ admin.get("/stats", async ({ headers, status }) => {
|
|||
tierCostBreakdown,
|
||||
totalTierCost: Math.round(totalTierCost * 100) / 100,
|
||||
avgCostPerHour,
|
||||
shopRealCost: {
|
||||
luckWinItemsCost: Math.round(luckWinDollarCost * 100) / 100,
|
||||
luckWinCount: luckWinOrders.length,
|
||||
consolationShippingCost: Math.round(consolationDollarCost * 100) / 100,
|
||||
consolationCount: Number(consolationCount[0]?.count || 0),
|
||||
totalRealCost: Math.round(totalRealCost * 100) / 100,
|
||||
realCostPerHour,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,15 @@
|
|||
totalCost: number;
|
||||
}
|
||||
|
||||
interface ShopRealCost {
|
||||
luckWinItemsCost: number;
|
||||
luckWinCount: number;
|
||||
consolationShippingCost: number;
|
||||
consolationCount: number;
|
||||
totalRealCost: number;
|
||||
realCostPerHour: number;
|
||||
}
|
||||
|
||||
interface Stats {
|
||||
totalUsers: number;
|
||||
totalProjects: number;
|
||||
|
|
@ -56,6 +65,7 @@
|
|||
tierCostBreakdown?: TierCost[];
|
||||
totalTierCost?: number;
|
||||
avgCostPerHour?: number;
|
||||
shopRealCost?: ShopRealCost;
|
||||
}
|
||||
|
||||
interface PendingProject {
|
||||
|
|
@ -516,6 +526,63 @@
|
|||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if stats.shopRealCost}
|
||||
<h2 class="mt-10 mb-6 text-2xl font-bold">real fulfillment cost</h2>
|
||||
<div class="grid grid-cols-1 gap-6 md:grid-cols-2">
|
||||
<div class="flex items-center gap-4 rounded-2xl border-4 border-red-500 bg-red-50 p-6">
|
||||
<div
|
||||
class="flex h-16 w-16 items-center justify-center rounded-full bg-red-600 text-white"
|
||||
>
|
||||
<DollarSign size={32} />
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm font-bold text-gray-500">real cost per hour</p>
|
||||
<p class="text-4xl font-bold text-red-700">
|
||||
${stats.shopRealCost.realCostPerHour.toFixed(2)}
|
||||
</p>
|
||||
<p class="text-xs text-gray-400">fulfillment cost ÷ shipped hours</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4 rounded-2xl border-4 border-red-500 bg-red-50 p-6">
|
||||
<div
|
||||
class="flex h-16 w-16 items-center justify-center rounded-full bg-red-600 text-white"
|
||||
>
|
||||
<DollarSign size={32} />
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm font-bold text-gray-500">total real cost</p>
|
||||
<p class="text-4xl font-bold text-red-700">
|
||||
${stats.shopRealCost.totalRealCost.toFixed(2)}
|
||||
</p>
|
||||
<p class="text-xs text-gray-400">items won + consolation shipping</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4 rounded-2xl border-4 border-black p-6">
|
||||
<div class="flex h-16 w-16 items-center justify-center rounded-full bg-black text-white">
|
||||
<Coins size={32} />
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm font-bold text-gray-500">luck win items</p>
|
||||
<p class="text-4xl font-bold">${stats.shopRealCost.luckWinItemsCost.toFixed(2)}</p>
|
||||
<p class="text-xs text-gray-400">{stats.shopRealCost.luckWinCount} items won</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4 rounded-2xl border-4 border-black p-6">
|
||||
<div class="flex h-16 w-16 items-center justify-center rounded-full bg-black text-white">
|
||||
<Coins size={32} />
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm font-bold text-gray-500">consolation shipping</p>
|
||||
<p class="text-4xl font-bold">${stats.shopRealCost.consolationShippingCost.toFixed(2)}</p>
|
||||
<p class="text-xs text-gray-400">{stats.shopRealCost.consolationCount} consolations × $2</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue