feat: show retail dashboard figures to the super admin only
پنل: بخشهای «تک فروشی» داشبورد (کارت، نمودار روند و تفکیک عمده/تک) فقط برای مدیر اصلی نمایش داده میشود. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Haiku 5.5
parent
0520aaf1b1
commit
640a42da05
@@ -1,6 +1,8 @@
|
||||
import { Controller, Get, Query } from '@nestjs/common';
|
||||
import { AdminDashboardService } from './admin-dashboard.service';
|
||||
import { RequirePermission } from '../auth/guards';
|
||||
import { CurrentAdmin, RequirePermission } from '../auth/guards';
|
||||
import type { AuthenticatedAdmin } from '../auth/jwt.strategy';
|
||||
import { isSuperAdmin } from '../auth/super-admin';
|
||||
|
||||
/**
|
||||
* Legacy shipped two dashboards behind two rids:
|
||||
@@ -18,8 +20,8 @@ export class AdminDashboardController {
|
||||
|
||||
@Get('overview')
|
||||
@RequirePermission('dashboard.view')
|
||||
overview() {
|
||||
return this.dashboard.overview();
|
||||
overview(@CurrentAdmin() admin: AuthenticatedAdmin) {
|
||||
return this.dashboard.overview(isSuperAdmin(admin.mobile));
|
||||
}
|
||||
|
||||
/** @deprecated superseded by GET overview; kept as an alias. */
|
||||
|
||||
@@ -37,8 +37,12 @@ export class AdminDashboardService {
|
||||
status: { in: [OrderStatus.DELIVERED_TO_AGENT, OrderStatus.DELIVERED_TO_CUSTOMER] },
|
||||
};
|
||||
|
||||
/** The single merged dashboard endpoint (`GET admin/dashboard/overview`). */
|
||||
async overview() {
|
||||
/**
|
||||
* The single merged dashboard endpoint (`GET admin/dashboard/overview`).
|
||||
* Retail («تک فروشی») figures are only built for the super admin; for anyone
|
||||
* else they never leave the server.
|
||||
*/
|
||||
async overview(viewerIsSuperAdmin = false) {
|
||||
const since30 = this.daysAgo(30);
|
||||
const since7 = this.daysAgo(7);
|
||||
|
||||
@@ -111,7 +115,11 @@ export class AdminDashboardService {
|
||||
if (o.channel === 'WHOLESALE') bucket.wholesale += 1;
|
||||
else bucket.retail += 1;
|
||||
}
|
||||
const ordersTrend = [...trendByDay.entries()].map(([date, v]) => ({ date, ...v }));
|
||||
// Non-super admins get wholesale only: their `total` is the wholesale count
|
||||
// and there is no `retail` field, so the retail number cannot be derived.
|
||||
const ordersTrend = [...trendByDay.entries()].map(([date, v]) =>
|
||||
viewerIsSuperAdmin ? { date, ...v } : { date, total: v.wholesale, wholesale: v.wholesale },
|
||||
);
|
||||
|
||||
const statusLabels: Record<OrderStatus, string> = {
|
||||
SUSPENDED: 'معلق (سقف اعتبار)',
|
||||
@@ -130,12 +138,14 @@ export class AdminDashboardService {
|
||||
}));
|
||||
|
||||
const channelLabels: Record<string, string> = { RETAIL: 'تک فروشی', WHOLESALE: 'عمده فروشی' };
|
||||
const channelBreakdown = channelGroups.map((g) => ({
|
||||
channel: g.channel,
|
||||
label: channelLabels[g.channel] ?? g.channel,
|
||||
count: g._count,
|
||||
grams: g._sum.totalGoldWeightGrams ?? new Prisma.Decimal(0),
|
||||
}));
|
||||
const channelBreakdown = viewerIsSuperAdmin
|
||||
? channelGroups.map((g) => ({
|
||||
channel: g.channel,
|
||||
label: channelLabels[g.channel] ?? g.channel,
|
||||
count: g._count,
|
||||
grams: g._sum.totalGoldWeightGrams ?? new Prisma.Decimal(0),
|
||||
}))
|
||||
: [];
|
||||
|
||||
const productTotals = new Map<string, { title: string; weight: Prisma.Decimal; qty: number }>();
|
||||
for (const it of topProductRows) {
|
||||
|
||||
Reference in New Issue
Block a user