Add defaultLocale, EN names, and dashboard activity endpoints.
Expose branding.defaultLocale, optional user EN name fields, and lightweight dual daily activity for orders/customers; update project context and rules. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
4598add88c
commit
43e3912662
@@ -19,6 +19,7 @@ import {
|
||||
ListOrdersDto,
|
||||
UpdateOrderDto,
|
||||
} from './dto/order.dto';
|
||||
import { DailyActivityQueryDto } from '../common/dto/daily-activity-query.dto';
|
||||
import { OrdersService } from './orders.service';
|
||||
|
||||
@Controller('businesses/:businessId/orders')
|
||||
@@ -35,6 +36,15 @@ export class OrdersController {
|
||||
return this.service.list(businessId, query, user);
|
||||
}
|
||||
|
||||
@Get('activity')
|
||||
activity(
|
||||
@Param('businessId') businessId: string,
|
||||
@Query() query: DailyActivityQueryDto,
|
||||
@CurrentUser() user: AuthUser,
|
||||
) {
|
||||
return this.service.dailyActivity(businessId, query.days, user);
|
||||
}
|
||||
|
||||
@Get(':orderId')
|
||||
getOne(
|
||||
@Param('businessId') businessId: string,
|
||||
|
||||
@@ -17,6 +17,10 @@ import { AuthUser } from '../auth/auth.types';
|
||||
import { BusinessSettingsService } from '../business-settings/business-settings.service';
|
||||
import { PermissionsService } from '../auth/permissions.service';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import {
|
||||
buildDualDailyActivitySeries,
|
||||
startOfLocalDay,
|
||||
} from '../common/daily-activity';
|
||||
import {
|
||||
CreateAdminOrderDto,
|
||||
ListOrdersDto,
|
||||
@@ -168,6 +172,50 @@ export class OrdersService {
|
||||
};
|
||||
}
|
||||
|
||||
/** Lightweight daily counts for dashboard charts (admin only). */
|
||||
async dailyActivity(
|
||||
businessIdRaw: string,
|
||||
daysRaw: number | undefined,
|
||||
actor: AuthUser,
|
||||
) {
|
||||
const businessId = BigInt(businessIdRaw);
|
||||
const scope = await this.resolveReadScope(businessId, actor);
|
||||
if (scope !== 'admin') {
|
||||
throw new ForbiddenException('Missing permission: orders.read for this business');
|
||||
}
|
||||
|
||||
const days = Math.min(Math.max(daysRaw ?? 30, 1), 90);
|
||||
const from = startOfLocalDay(days - 1);
|
||||
|
||||
const [orderRows, cartRows] = await Promise.all([
|
||||
this.prisma.$queryRaw<{ day: Date; count: number }[]>(
|
||||
Prisma.sql`
|
||||
SELECT created_at::date AS day,
|
||||
COUNT(*)::int AS count
|
||||
FROM orders
|
||||
WHERE business_id = ${businessId}
|
||||
AND created_at >= ${from}
|
||||
GROUP BY 1
|
||||
ORDER BY 1
|
||||
`,
|
||||
),
|
||||
this.prisma.$queryRaw<{ day: Date; count: number }[]>(
|
||||
Prisma.sql`
|
||||
SELECT ci.created_at::date AS day,
|
||||
COUNT(*)::int AS count
|
||||
FROM cart_items ci
|
||||
JOIN carts c ON c.id = ci.cart_id
|
||||
WHERE c.business_id = ${businessId}
|
||||
AND ci.created_at >= ${from}
|
||||
GROUP BY 1
|
||||
ORDER BY 1
|
||||
`,
|
||||
),
|
||||
]);
|
||||
|
||||
return buildDualDailyActivitySeries(days, orderRows, cartRows);
|
||||
}
|
||||
|
||||
async getOne(businessIdRaw: string, orderIdRaw: string, actor: AuthUser) {
|
||||
const businessId = BigInt(businessIdRaw);
|
||||
const orderId = BigInt(orderIdRaw);
|
||||
@@ -788,7 +836,7 @@ export class OrdersService {
|
||||
private serialize(
|
||||
order: OrderWithItems,
|
||||
galleryByProductId: Map<string, string> = new Map(),
|
||||
processSteps: { id: string; label: string; color: string }[] = [],
|
||||
processSteps: { id: string; label: string; labelFa: string; color: string }[] = [],
|
||||
) {
|
||||
const processStep =
|
||||
processSteps.find((step) => step.id === order.processStepId) ?? null;
|
||||
@@ -800,6 +848,7 @@ export class OrdersService {
|
||||
status: order.status,
|
||||
processStepId: order.processStepId,
|
||||
processStepLabel: processStep?.label ?? null,
|
||||
processStepLabelFa: processStep?.labelFa?.trim() || null,
|
||||
processStepColor: processStep?.color ?? null,
|
||||
source: order.source,
|
||||
subtotal: Number(order.subtotal),
|
||||
|
||||
Reference in New Issue
Block a user