build
This commit is contained in:
+121
-4
@@ -84977,8 +84977,7 @@ var require_acl_constants = __commonJS({
|
||||
"config.wallet",
|
||||
"custom.config.statics.slider",
|
||||
"custom.config.statics.tags.banner.ids",
|
||||
"dashboard.omde",
|
||||
"dashboard.tak",
|
||||
"dashboard.view",
|
||||
"document.create",
|
||||
"document.list",
|
||||
"document.list.confirm",
|
||||
@@ -92609,6 +92608,112 @@ var require_admin_dashboard_service = __commonJS({
|
||||
static SOLD = {
|
||||
status: { in: [client_1.OrderStatus.DELIVERED_TO_AGENT, client_1.OrderStatus.DELIVERED_TO_CUSTOMER] }
|
||||
};
|
||||
/** The single merged dashboard endpoint (`GET admin/dashboard/overview`). */
|
||||
async overview() {
|
||||
const since30 = this.daysAgo(30);
|
||||
const since7 = this.daysAgo(7);
|
||||
const [omde, tak, ordersLast30, statusGroups, channelGroups, topProductRows, latestGoldQuote, debtAgg] = await Promise.all([
|
||||
this.omdeAnalytics(),
|
||||
this.takAnalytics(),
|
||||
// روند سفارشها در ۳۰ روز گذشته — one row per order, bucketed by day below.
|
||||
this.prisma.order.findMany({
|
||||
where: { createdAt: { gte: since30 } },
|
||||
select: { createdAt: true, channel: true }
|
||||
}),
|
||||
// دونات وضعیت سفارشات (۳۰ روز گذشته)
|
||||
this.prisma.order.groupBy({
|
||||
by: ["status"],
|
||||
where: { createdAt: { gte: since30 } },
|
||||
_count: true
|
||||
}),
|
||||
// تفکیک عمده/تک (۳۰ روز گذشته)
|
||||
this.prisma.order.groupBy({
|
||||
by: ["channel"],
|
||||
where: { createdAt: { gte: since30 } },
|
||||
_count: true,
|
||||
_sum: { totalGoldWeightGrams: true }
|
||||
}),
|
||||
// محصولات پرفروش (۳۰ روز گذشته) — by shipped weight, top 6.
|
||||
this.prisma.orderItem.findMany({
|
||||
where: { order: { createdAt: { gte: since30 } } },
|
||||
select: {
|
||||
quantity: true,
|
||||
computedWeightGrams: true,
|
||||
productVariant: { select: { product: { select: { id: true, title: true } } } }
|
||||
}
|
||||
}),
|
||||
// نرخ فعلی طلا — legacy's goldPrice widget (18 عیار / 750‰).
|
||||
this.prisma.goldPriceQuote.findFirst({
|
||||
where: { karatPerMille: 750 },
|
||||
orderBy: { quotedAt: "desc" }
|
||||
}),
|
||||
// مجموع بدهی مشتریان (گرم طلا) — cached balance, see Customer.goldDebtGrams.
|
||||
this.prisma.customer.aggregate({ _sum: { goldDebtGrams: true } })
|
||||
]);
|
||||
const trendByDay = /* @__PURE__ */ new Map();
|
||||
for (let i = 29; i >= 0; i--) {
|
||||
trendByDay.set(this.dayKey(this.daysAgo(i)), { total: 0, retail: 0, wholesale: 0 });
|
||||
}
|
||||
for (const o of ordersLast30) {
|
||||
const key = this.dayKey(o.createdAt);
|
||||
const bucket = trendByDay.get(key);
|
||||
if (!bucket)
|
||||
continue;
|
||||
bucket.total += 1;
|
||||
if (o.channel === "WHOLESALE")
|
||||
bucket.wholesale += 1;
|
||||
else
|
||||
bucket.retail += 1;
|
||||
}
|
||||
const ordersTrend = [...trendByDay.entries()].map(([date, v]) => ({ date, ...v }));
|
||||
const statusLabels = {
|
||||
SUSPENDED: "\u0645\u0639\u0644\u0642 (\u0633\u0642\u0641 \u0627\u0639\u062A\u0628\u0627\u0631)",
|
||||
PLACED: "\u062B\u0628\u062A \u0634\u062F\u0647",
|
||||
ADMIN_APPROVED: "\u062A\u0627\u06CC\u06CC\u062F \u0645\u062F\u06CC\u0631",
|
||||
SOURCING: "\u062F\u0631 \u062D\u0627\u0644 \u062A\u0627\u0645\u06CC\u0646",
|
||||
PREPARING: "\u062F\u0631 \u062D\u0627\u0644 \u0622\u0645\u0627\u062F\u0647\u200C\u0633\u0627\u0632\u06CC",
|
||||
DELIVERED_TO_AGENT: "\u062A\u062D\u0648\u06CC\u0644 \u0628\u0647 \u0646\u0645\u0627\u06CC\u0646\u062F\u0647",
|
||||
DELIVERED_TO_CUSTOMER: "\u062A\u062D\u0648\u06CC\u0644 \u0628\u0647 \u0645\u0634\u062A\u0631\u06CC",
|
||||
CANCELLED: "\u0644\u063A\u0648 \u0634\u062F\u0647"
|
||||
};
|
||||
const statusBreakdown = statusGroups.map((g) => ({
|
||||
status: g.status,
|
||||
label: statusLabels[g.status],
|
||||
count: g._count
|
||||
}));
|
||||
const channelLabels = { RETAIL: "\u062A\u06A9 \u0641\u0631\u0648\u0634\u06CC", WHOLESALE: "\u0639\u0645\u062F\u0647 \u0641\u0631\u0648\u0634\u06CC" };
|
||||
const channelBreakdown = channelGroups.map((g) => ({
|
||||
channel: g.channel,
|
||||
label: channelLabels[g.channel] ?? g.channel,
|
||||
count: g._count,
|
||||
grams: g._sum.totalGoldWeightGrams ?? new client_1.Prisma.Decimal(0)
|
||||
}));
|
||||
const productTotals = /* @__PURE__ */ new Map();
|
||||
for (const it of topProductRows) {
|
||||
const p = it.productVariant.product;
|
||||
const row = productTotals.get(p.id) ?? { title: p.title, weight: new client_1.Prisma.Decimal(0), qty: 0 };
|
||||
row.weight = row.weight.add(it.computedWeightGrams.mul(it.quantity));
|
||||
row.qty += it.quantity;
|
||||
productTotals.set(p.id, row);
|
||||
}
|
||||
const topProducts = [...productTotals.values()].sort((a, b) => Number(b.weight) - Number(a.weight)).slice(0, 6);
|
||||
return {
|
||||
// ── everything the two legacy dashboards returned, unchanged ──────
|
||||
...omde,
|
||||
takLastOrder: tak.lastOrder,
|
||||
// ── new chart data ─────────────────────────────────────────────
|
||||
ordersTrend,
|
||||
statusBreakdown,
|
||||
channelBreakdown,
|
||||
topProducts,
|
||||
goldPricePerGram: latestGoldQuote?.pricePerGramRial ?? null,
|
||||
goldPriceQuotedAt: latestGoldQuote?.quotedAt ?? null,
|
||||
totalCustomerDebtGrams: debtAgg._sum.goldDebtGrams ?? new client_1.Prisma.Decimal(0)
|
||||
};
|
||||
}
|
||||
dayKey(d) {
|
||||
return d.toISOString().slice(0, 10);
|
||||
}
|
||||
/** Legacy `GET get-analytics` — the wholesale dashboard. */
|
||||
async omdeAnalytics() {
|
||||
const since30 = this.daysAgo(30);
|
||||
@@ -92837,9 +92942,14 @@ var require_admin_dashboard_controller = __commonJS({
|
||||
constructor(dashboard) {
|
||||
this.dashboard = dashboard;
|
||||
}
|
||||
overview() {
|
||||
return this.dashboard.overview();
|
||||
}
|
||||
/** @deprecated superseded by GET overview; kept as an alias. */
|
||||
omde() {
|
||||
return this.dashboard.omdeAnalytics();
|
||||
}
|
||||
/** @deprecated superseded by GET overview; kept as an alias. */
|
||||
tak() {
|
||||
return this.dashboard.takAnalytics();
|
||||
}
|
||||
@@ -92853,16 +92963,23 @@ var require_admin_dashboard_controller = __commonJS({
|
||||
}
|
||||
};
|
||||
exports2.AdminDashboardController = AdminDashboardController;
|
||||
__decorate4([
|
||||
(0, common_12.Get)("overview"),
|
||||
(0, guards_1.RequirePermission)("dashboard.view"),
|
||||
__metadata3("design:type", Function),
|
||||
__metadata3("design:paramtypes", []),
|
||||
__metadata3("design:returntype", void 0)
|
||||
], AdminDashboardController.prototype, "overview", null);
|
||||
__decorate4([
|
||||
(0, common_12.Get)("omde"),
|
||||
(0, guards_1.RequirePermission)("dashboard.omde"),
|
||||
(0, guards_1.RequirePermission)("dashboard.view"),
|
||||
__metadata3("design:type", Function),
|
||||
__metadata3("design:paramtypes", []),
|
||||
__metadata3("design:returntype", void 0)
|
||||
], AdminDashboardController.prototype, "omde", null);
|
||||
__decorate4([
|
||||
(0, common_12.Get)("tak"),
|
||||
(0, guards_1.RequirePermission)("dashboard.tak"),
|
||||
(0, guards_1.RequirePermission)("dashboard.view"),
|
||||
__metadata3("design:type", Function),
|
||||
__metadata3("design:paramtypes", []),
|
||||
__metadata3("design:returntype", void 0)
|
||||
|
||||
@@ -1620,8 +1620,7 @@ var ALL_RIDS = [
|
||||
"config.wallet",
|
||||
"custom.config.statics.slider",
|
||||
"custom.config.statics.tags.banner.ids",
|
||||
"dashboard.omde",
|
||||
"dashboard.tak",
|
||||
"dashboard.view",
|
||||
"document.create",
|
||||
"document.list",
|
||||
"document.list.confirm",
|
||||
@@ -1782,7 +1781,7 @@ async function main() {
|
||||
"persons.list.client.single.view",
|
||||
"persons.create",
|
||||
"product.list",
|
||||
"dashboard.tak",
|
||||
"dashboard.view",
|
||||
"comment"
|
||||
]
|
||||
},
|
||||
@@ -1800,7 +1799,7 @@ async function main() {
|
||||
"report.order.item",
|
||||
"report.product",
|
||||
"config.report",
|
||||
"dashboard.omde"
|
||||
"dashboard.view"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user