Files
fabric_trade_server/dist/dashboard/dashboard.service.js
T
mohmdsdgandCursor cbcea58e94 Deploy via committed dist: local Nest build, server runs with .env
Build on the developer machine without Docker, commit dist/, and run on the server with Dockerfile.dist plus compose env_file.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-05 21:35:22 +03:30

219 lines
9.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DashboardService = void 0;
const common_1 = require("@nestjs/common");
const prisma_service_1 = require("../prisma/prisma.service");
const order_status_1 = require("../common/utils/order-status");
const orders_service_1 = require("../orders/orders.service");
let DashboardService = class DashboardService {
prisma;
ordersService;
constructor(prisma, ordersService) {
this.prisma = prisma;
this.ordersService = ordersService;
}
async getDashboard() {
const now = new Date();
const startOfToday = new Date(now);
startOfToday.setHours(0, 0, 0, 0);
const startOfYesterday = new Date(startOfToday);
startOfYesterday.setDate(startOfYesterday.getDate() - 1);
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
const startOfChart = new Date(startOfToday);
startOfChart.setDate(startOfChart.getDate() - 27);
const [newOrders, pendingReview, pendingCheck, finalApproval, todayOrders, yesterdayOrders, monthOrders, chartOrders, recentOrders, checks, notifications, messages,] = await Promise.all([
this.prisma.order.count({
where: { createdAt: { gte: startOfToday } },
}),
this.prisma.order.count({
where: { status: { in: order_status_1.ADMIN_STATUS_MAP.pending_review } },
}),
this.prisma.order.count({
where: { status: { in: order_status_1.ADMIN_STATUS_MAP.pending_check } },
}),
this.prisma.order.count({
where: { status: { in: order_status_1.ADMIN_STATUS_MAP.final_approval } },
}),
this.prisma.order.findMany({
where: { createdAt: { gte: startOfToday } },
select: { finalAmount: true, totalAmount: true },
}),
this.prisma.order.findMany({
where: {
createdAt: { gte: startOfYesterday, lt: startOfToday },
},
select: { finalAmount: true, totalAmount: true },
}),
this.prisma.order.findMany({
where: { createdAt: { gte: startOfMonth } },
select: { finalAmount: true, totalAmount: true },
}),
this.prisma.order.findMany({
where: { createdAt: { gte: startOfChart } },
select: {
createdAt: true,
finalAmount: true,
totalAmount: true,
},
}),
this.prisma.order.findMany({
orderBy: { createdAt: 'desc' },
take: 5,
include: {
items: true,
checks: { orderBy: { row: 'asc' } },
cashReceipt: true,
statusHistory: { orderBy: { changedAt: 'asc' } },
shipment: true,
user: true,
company: true,
},
}),
this.prisma.check.findMany({
where: {
status: { not: 'rejected' },
dueDate: {
gte: startOfToday,
lte: new Date(startOfToday.getTime() + 30 * 24 * 60 * 60 * 1000),
},
},
select: { dueDate: true, amount: true },
}),
this.prisma.adminNotification.findMany({
orderBy: { sentAt: 'desc' },
take: 20,
}),
this.prisma.internalMessage.findMany({
orderBy: { sentAt: 'desc' },
take: 20,
}),
]);
const sumAmounts = (rows) => rows.reduce((s, o) => s + (o.finalAmount ?? o.totalAmount ?? 0), 0);
const todaySales = sumAmounts(todayOrders);
const yesterdaySales = sumAmounts(yesterdayOrders);
const monthSales = sumAmounts(monthOrders);
const todaySalesGrowth = yesterdaySales > 0
? Math.round(((todaySales - yesterdaySales) / yesterdaySales) * 100)
: todaySales > 0
? 100
: 0;
const salesByDay = Array.from({ length: 28 }, () => 0);
for (const order of chartOrders) {
const dayIndex = Math.floor((order.createdAt.getTime() - startOfChart.getTime()) /
(24 * 60 * 60 * 1000));
if (dayIndex >= 0 && dayIndex < 28) {
salesByDay[dayIndex] += order.finalAmount ?? order.totalAmount ?? 0;
}
}
const maxSale = Math.max(...salesByDay, 1);
const salesChartData = salesByDay.map((v) => Number((v / maxSale).toFixed(3)));
const checkDeadlines = this.buildCheckDeadlines(checks, startOfToday);
return {
stats: {
newOrders,
pendingReview,
pendingCheck,
finalApproval,
todaySales,
todaySalesGrowth,
monthSales,
},
recentOrders: recentOrders.map((o) => this.ordersService.mapOrder(o, true)),
salesChartData,
chartAxisLabels: ['۱', '۷', '۱۴', '۲۱', '۲۸'],
checkDeadlines,
notifications: notifications.map((n) => ({
id: n.id,
title: n.title,
body: n.body,
tone: n.tone,
link: n.link,
isRead: n.isRead,
sentAt: n.sentAt.toISOString(),
})),
messages: messages.map((m) => ({
id: m.id,
senderName: m.senderName,
subject: m.subject,
preview: m.preview,
isRead: m.isRead,
sentAt: m.sentAt.toISOString(),
})),
};
}
async getReports() {
const orders = await this.prisma.order.findMany({
select: {
companyName: true,
status: true,
totalMeterage: true,
finalAmount: true,
totalAmount: true,
},
});
const byCompany = new Map();
const byStatus = new Map();
for (const order of orders) {
const company = byCompany.get(order.companyName) ?? {
companyName: order.companyName,
orderCount: 0,
totalAmount: 0,
meterage: 0,
};
company.orderCount += 1;
company.totalAmount += order.finalAmount ?? order.totalAmount ?? 0;
company.meterage += order.totalMeterage;
byCompany.set(order.companyName, company);
const label = order_status_1.ORDER_STATUS_LABELS[order.status] ?? (0, order_status_1.toAdminStatus)(order.status);
byStatus.set(label, (byStatus.get(label) ?? 0) + 1);
}
return {
reportSalesByCompany: Array.from(byCompany.values()).sort((a, b) => b.totalAmount - a.totalAmount),
reportOrdersByStatus: Array.from(byStatus.entries()).map(([status, count]) => ({ status, count })),
};
}
buildCheckDeadlines(checks, startOfToday) {
const buckets = [
{ id: 'cd-today', label: 'امروز', tone: 'danger', days: 0 },
{ id: 'cd-3', label: '۳ روز آینده', tone: 'warning', days: 3 },
{ id: 'cd-7', label: '۷ روز آینده', tone: 'warning', days: 7 },
{ id: 'cd-30', label: '۳۰ روز آینده', tone: 'neutral', days: 30 },
];
return buckets.map((bucket) => {
const end = new Date(startOfToday);
end.setDate(end.getDate() + bucket.days);
end.setHours(23, 59, 59, 999);
const matched = checks.filter((c) => {
const d = new Date(c.dueDate);
d.setHours(0, 0, 0, 0);
if (bucket.days === 0) {
return d.getTime() === startOfToday.getTime();
}
return d >= startOfToday && d <= end;
});
return {
id: bucket.id,
label: bucket.label,
tone: bucket.tone,
count: matched.length,
amount: matched.reduce((s, c) => s + c.amount, 0),
};
});
}
};
exports.DashboardService = DashboardService;
exports.DashboardService = DashboardService = __decorate([
(0, common_1.Injectable)(),
__metadata("design:paramtypes", [prisma_service_1.PrismaService,
orders_service_1.OrdersService])
], DashboardService);
//# sourceMappingURL=dashboard.service.js.map