mirror of
https://github.com/xCyanGrizzly/DragonsStash.git
synced 2026-05-11 06:11:15 +00:00
Init
This commit is contained in:
167
src/data/dashboard.queries.ts
Normal file
167
src/data/dashboard.queries.ts
Normal file
@@ -0,0 +1,167 @@
|
||||
import { prisma } from "@/lib/prisma";
|
||||
|
||||
interface LowStockItem {
|
||||
id: string;
|
||||
name: string;
|
||||
type: "filament" | "resin" | "paint";
|
||||
colorHex: string;
|
||||
remaining: number;
|
||||
total: number;
|
||||
percent: number;
|
||||
}
|
||||
|
||||
interface RecentUsage {
|
||||
id: string;
|
||||
itemType: string;
|
||||
amount: number;
|
||||
unit: string;
|
||||
notes: string | null;
|
||||
createdAt: Date;
|
||||
itemName: string;
|
||||
}
|
||||
|
||||
export interface DashboardStats {
|
||||
totalItems: number;
|
||||
inventoryValue: number;
|
||||
lowStockCount: number;
|
||||
recentActivityCount: number;
|
||||
lowStockItems: LowStockItem[];
|
||||
recentUsage: RecentUsage[];
|
||||
}
|
||||
|
||||
export async function getDashboardStats(
|
||||
userId: string,
|
||||
lowStockThreshold: number
|
||||
): Promise<DashboardStats> {
|
||||
const [filaments, resins, paints, usageLogs24h, recentLogs] = await Promise.all([
|
||||
prisma.filament.findMany({
|
||||
where: { userId, archived: false },
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
colorHex: true,
|
||||
spoolWeight: true,
|
||||
usedWeight: true,
|
||||
cost: true,
|
||||
},
|
||||
}),
|
||||
prisma.resin.findMany({
|
||||
where: { userId, archived: false },
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
colorHex: true,
|
||||
bottleSize: true,
|
||||
usedML: true,
|
||||
cost: true,
|
||||
},
|
||||
}),
|
||||
prisma.paint.findMany({
|
||||
where: { userId, archived: false },
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
colorHex: true,
|
||||
volumeML: true,
|
||||
usedML: true,
|
||||
cost: true,
|
||||
},
|
||||
}),
|
||||
prisma.usageLog.count({
|
||||
where: {
|
||||
userId,
|
||||
createdAt: { gte: new Date(Date.now() - 24 * 60 * 60 * 1000) },
|
||||
},
|
||||
}),
|
||||
prisma.usageLog.findMany({
|
||||
where: { userId },
|
||||
orderBy: { createdAt: "desc" },
|
||||
take: 10,
|
||||
include: {
|
||||
filament: { select: { name: true } },
|
||||
resin: { select: { name: true } },
|
||||
paint: { select: { name: true } },
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
const totalItems = filaments.length + resins.length + paints.length;
|
||||
|
||||
const inventoryValue =
|
||||
filaments.reduce((sum: number, f: { cost: number | null }) => sum + (f.cost ?? 0), 0) +
|
||||
resins.reduce((sum: number, r: { cost: number | null }) => sum + (r.cost ?? 0), 0) +
|
||||
paints.reduce((sum: number, p: { cost: number | null }) => sum + (p.cost ?? 0), 0);
|
||||
|
||||
const lowStockItems: LowStockItem[] = [];
|
||||
|
||||
for (const f of filaments) {
|
||||
const remaining = f.spoolWeight - f.usedWeight;
|
||||
const percent = f.spoolWeight > 0 ? (remaining / f.spoolWeight) * 100 : 0;
|
||||
if (percent <= lowStockThreshold && percent > 0) {
|
||||
lowStockItems.push({
|
||||
id: f.id,
|
||||
name: f.name,
|
||||
type: "filament",
|
||||
colorHex: f.colorHex,
|
||||
remaining,
|
||||
total: f.spoolWeight,
|
||||
percent,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (const r of resins) {
|
||||
const remaining = r.bottleSize - r.usedML;
|
||||
const percent = r.bottleSize > 0 ? (remaining / r.bottleSize) * 100 : 0;
|
||||
if (percent <= lowStockThreshold && percent > 0) {
|
||||
lowStockItems.push({
|
||||
id: r.id,
|
||||
name: r.name,
|
||||
type: "resin",
|
||||
colorHex: r.colorHex,
|
||||
remaining,
|
||||
total: r.bottleSize,
|
||||
percent,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (const p of paints) {
|
||||
const remaining = p.volumeML - p.usedML;
|
||||
const percent = p.volumeML > 0 ? (remaining / p.volumeML) * 100 : 0;
|
||||
if (percent <= lowStockThreshold && percent > 0) {
|
||||
lowStockItems.push({
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
type: "paint",
|
||||
colorHex: p.colorHex,
|
||||
remaining,
|
||||
total: p.volumeML,
|
||||
percent,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
lowStockItems.sort((a, b) => a.percent - b.percent);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const recentUsage: RecentUsage[] = recentLogs.map((log: any) => ({
|
||||
id: log.id,
|
||||
itemType: log.itemType,
|
||||
amount: log.amount,
|
||||
unit: log.unit,
|
||||
notes: log.notes,
|
||||
createdAt: log.createdAt,
|
||||
itemName:
|
||||
log.filament?.name ?? log.resin?.name ?? log.paint?.name ?? "Unknown",
|
||||
}));
|
||||
|
||||
return {
|
||||
totalItems,
|
||||
inventoryValue,
|
||||
lowStockCount: lowStockItems.length,
|
||||
recentActivityCount: usageLogs24h,
|
||||
lowStockItems,
|
||||
recentUsage,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user